From 9926179e7f1c9661edfa3dd11209a2afb5e3a925 Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Thu, 28 Mar 2019 14:27:34 +1100 Subject: [PATCH] map/fixed: add static sized flat map --- CMakeLists.txt | 3 + map/fixed.cpp | 1 + map/fixed.hpp | 157 +++++++++++++++++++++++++++++++++++++++++++++ test/map/fixed.cpp | 26 ++++++++ 4 files changed, 187 insertions(+) create mode 100644 map/fixed.cpp create mode 100644 map/fixed.hpp create mode 100644 test/map/fixed.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 238c8c9d..01b354ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/map/fixed.cpp b/map/fixed.cpp new file mode 100644 index 00000000..42dbcf51 --- /dev/null +++ b/map/fixed.cpp @@ -0,0 +1 @@ +#include "fixed.hpp" \ No newline at end of file diff --git a/map/fixed.hpp b/map/fixed.hpp new file mode 100644 index 00000000..f15d8f30 --- /dev/null +++ b/map/fixed.hpp @@ -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 + */ + +#pragma once + +#include + +#include +#include +#include + +#include + + +namespace cruft::map { + /// A flat map structure with a static capacity store and dynamic size. + template + class fixed { + public: + static constexpr auto elements = SizeV; + + using key_type = KeyT; + using mapped_type = ValueT; + using value_type = std::pair; + + using iterator = value_type*; + + + /////////////////////////////////////////////////////////////////////// + fixed () = default; + + + //--------------------------------------------------------------------- + fixed (std::initializer_list 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 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; + }; +} diff --git a/test/map/fixed.cpp b/test/map/fixed.cpp new file mode 100644 index 00000000..5ab21635 --- /dev/null +++ b/test/map/fixed.cpp @@ -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 ( + [&] () { map.insert ({ 4, "four" }); }, + "over-allocation throws bad_alloc" + ); + + return tap.status (); +} \ No newline at end of file