From a09d10433021fefc85c508e592eb7d3cf2fdbef5 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 18 Feb 2016 20:10:28 +0000 Subject: [PATCH 1/6] Added unordered_set to project --- test/vs2015/etl.vcxproj | 2 ++ test/vs2015/etl.vcxproj.filters | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/test/vs2015/etl.vcxproj b/test/vs2015/etl.vcxproj index 0292881a..48c7ee62 100644 --- a/test/vs2015/etl.vcxproj +++ b/test/vs2015/etl.vcxproj @@ -263,6 +263,7 @@ + @@ -367,6 +368,7 @@ + diff --git a/test/vs2015/etl.vcxproj.filters b/test/vs2015/etl.vcxproj.filters index 3bd86a0f..16b2425d 100644 --- a/test/vs2015/etl.vcxproj.filters +++ b/test/vs2015/etl.vcxproj.filters @@ -465,6 +465,9 @@ ETL\Containers + + ETL\Containers + @@ -719,6 +722,9 @@ Source Files + + Source Files + From 9908714b13c94e3642d9e4189f34954312152b5e Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 18 Feb 2016 20:10:54 +0000 Subject: [PATCH 2/6] Added begin==end tests --- test/test_intrusive_forward_list.cpp | 3 +++ test/test_intrusive_list.cpp | 3 +++ test/test_unordered_map.cpp | 1 + 3 files changed, 7 insertions(+) diff --git a/test/test_intrusive_forward_list.cpp b/test/test_intrusive_forward_list.cpp index df73665d..b3f7054e 100644 --- a/test/test_intrusive_forward_list.cpp +++ b/test/test_intrusive_forward_list.cpp @@ -183,6 +183,9 @@ namespace CHECK(data0.empty()); CHECK(data1.empty()); + + CHECK(data0.begin() == data0.end()); + CHECK(data1.begin() == data1.end()); } //************************************************************************* diff --git a/test/test_intrusive_list.cpp b/test/test_intrusive_list.cpp index 048a16a3..a7000dc7 100644 --- a/test/test_intrusive_list.cpp +++ b/test/test_intrusive_list.cpp @@ -180,6 +180,9 @@ namespace CHECK(data0.empty()); CHECK(data1.empty()); + + CHECK(data0.begin() == data0.end()); + CHECK(data1.begin() == data1.end()); } //************************************************************************* diff --git a/test/test_unordered_map.cpp b/test/test_unordered_map.cpp index f6246a4b..01af4012 100644 --- a/test/test_unordered_map.cpp +++ b/test/test_unordered_map.cpp @@ -188,6 +188,7 @@ namespace CHECK_EQUAL(data.size(), size_t(0)); CHECK(data.empty()); CHECK_EQUAL(data.max_size(), SIZE); + CHECK(data.begin() == data.end()); } //************************************************************************* From 6eb15a884c69b90e95929f4a9af4b783e0558ca1 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 18 Feb 2016 20:11:07 +0000 Subject: [PATCH 3/6] Added unordered_set to project --- test/test_unordered_set.cpp | 438 ++++++++++++++++++++++++++++++++++++ unordered_set.h | 123 ++++++++++ 2 files changed, 561 insertions(+) create mode 100644 test/test_unordered_set.cpp create mode 100644 unordered_set.h diff --git a/test/test_unordered_set.cpp b/test/test_unordered_set.cpp new file mode 100644 index 00000000..9721f89a --- /dev/null +++ b/test/test_unordered_set.cpp @@ -0,0 +1,438 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2016 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "data.h" + +#include "../unordered_set.h" +#include "../checksum.h" + +namespace +{ + SUITE(test_unordered_set) + { + static const size_t SIZE = 10; + + typedef TestDataDC DC; + typedef TestDataNDC NDC; + + struct simple_hash + { + size_t operator ()(const NDC& value) const + { + return etl::checksum(value.value.begin(), value.value.end()); + } + }; + + typedef etl::unordered_set DataDC; + typedef etl::unordered_set DataNDC; + typedef etl::iunordered_set IDataNDC; + + NDC N0 = NDC("FF"); + NDC N1 = NDC("FG"); + NDC N2 = NDC("FH"); + NDC N3 = NDC("FI"); + NDC N4 = NDC("FJ"); + NDC N5 = NDC("FK"); + NDC N6 = NDC("FL"); + NDC N7 = NDC("FM"); + NDC N8 = NDC("FN"); + NDC N9 = NDC("FO"); + NDC N10 = NDC("FP"); + NDC N11 = NDC("FQ"); + NDC N12 = NDC("FR"); + NDC N13 = NDC("FS"); + NDC N14 = NDC("FT"); + NDC N15 = NDC("FU"); + NDC N16 = NDC("FV"); + NDC N17 = NDC("FW"); + NDC N18 = NDC("FX"); + NDC N19 = NDC("FY"); + + std::vector initial_data; + std::vector excess_data; + std::vector different_data; + + //************************************************************************* + struct SetupFixture + { + SetupFixture() + { + NDC n[] = + { + N0, N1, N2, N3, N4, N5, N6, N7, N8, N9 + }; + + NDC n2[] = + { + N0, N1, N2, N3, N4, N5, N6, N7, N8, N9, N10 + }; + + NDC n3[] = + { + N10, N11, N12, N13, N14, N15, N16, N17, N18, N19 + }; + + initial_data.assign(std::begin(n), std::end(n)); + excess_data.assign(std::begin(n2), std::end(n2)); + different_data.assign(std::begin(n3), std::end(n3)); + } + }; + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_default_constructor) + { + DataDC data; + + CHECK_EQUAL(data.size(), size_t(0)); + CHECK(data.empty()); + CHECK_EQUAL(data.max_size(), SIZE); + CHECK(data.begin() == data.end()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_range) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + CHECK(data.size() == SIZE); + CHECK(!data.empty()); + CHECK(data.full()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_assignment) + { + DataNDC data(initial_data.begin(), initial_data.end()); + DataNDC other_data; + + other_data = data; + + bool isEqual = std::equal(data.begin(), + data.end(), + other_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_assignment_interface) + { + DataNDC data1(initial_data.begin(), initial_data.end()); + DataNDC data2; + + IDataNDC& idata1 = data1; + IDataNDC& idata2 = data2; + + idata2 = idata1; + + bool isEqual = std::equal(data1.begin(), + data1.end(), + data2.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_self_assignment) + { + DataNDC data(initial_data.begin(), initial_data.end()); + DataNDC other_data(data); + + other_data = other_data; + + bool isEqual = std::equal(data.begin(), + data.end(), + other_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_empty_full) + { + DataNDC data; + + CHECK(!data.full()); + CHECK(data.empty()); + + data.insert(initial_data.begin(), initial_data.end()); + + CHECK(data.full()); + CHECK(!data.empty()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_assign_range) + { + DataNDC data; + + data.assign(initial_data.begin(), initial_data.end()); + + DataNDC::iterator idata; + + for (size_t i = 0; i < 10; ++i) + { + idata = data.find(initial_data[i]); + CHECK(idata != data.end()); + } + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_value) + { + DataNDC data; + + data.insert(N0); // Inserted + data.insert(N2); // Inserted + data.insert(N1); // Inserted + data.insert(N11); // Duplicate hash. Inserted + data.insert(N3); // Inserted + + CHECK_EQUAL(5U, data.size()); + + DataNDC::iterator idata; + + idata = data.find(N0); + CHECK(idata != data.end()); + CHECK(*idata == N0); + + idata = data.find(N1); + CHECK(idata != data.end()); + CHECK(*idata == N1); + + idata = data.find(N2); + CHECK(idata != data.end()); + CHECK(*idata == N2); + + idata = data.find(N11); + CHECK(idata != data.end()); + CHECK(*idata == N11); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_value_excess) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + CHECK_THROW(data.insert(N10), etl::unordered_set_full); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_range) + { + DataNDC data; + + data.insert(initial_data.begin(), initial_data.end()); + + for (size_t i = 0; i < data.size(); ++i) + { + DataNDC::iterator idata = data.find(initial_data[i]); + CHECK(idata != data.end()); + } + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_range_excess) + { + DataNDC data; + + CHECK_THROW(data.insert(excess_data.begin(), excess_data.end()), etl::unordered_set_full); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_key) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + size_t count = data.erase(N5); + + CHECK_EQUAL(1, count); + + DataNDC::iterator idata = data.find(N5); + CHECK(idata == data.end()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_single) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + DataNDC::const_iterator idata = data.find(N5); + DataNDC::const_iterator inext = idata; + ++inext; + + DataNDC::const_iterator iafter = data.erase(idata); + idata = data.find(N5); + + CHECK(idata == data.end()); + CHECK(inext == iafter); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_range) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + DataNDC::iterator idata = data.find(N5); + DataNDC::iterator idata_end = data.find(N8); + + std::vector test; + + test.assign(data.begin(), data.end()); + + idata = data.erase(idata, idata_end); // Erase N5, N6, N7 + CHECK(idata == data.find(N8)); + + test.assign(data.begin(), data.end()); + + idata = data.find(N0); + CHECK(idata != data.end()); + + idata = data.find(N1); + CHECK(idata != data.end()); + + idata = data.find(N2); + CHECK(idata != data.end()); + + idata = data.find(N3); + CHECK(idata != data.end()); + + idata = data.find(N4); + CHECK(idata != data.end()); + + idata = data.find(N5); + CHECK(idata == data.end()); + + idata = data.find(N6); + CHECK(idata == data.end()); + + idata = data.find(N7); + CHECK(idata == data.end()); + + idata = data.find(N8); + CHECK(idata != data.end()); + + idata = data.find(N9); + CHECK(idata != data.end()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_clear) + { + DataNDC data(initial_data.begin(), initial_data.end()); + data.clear(); + + CHECK_EQUAL(data.size(), size_t(0)); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_count_key) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + size_t count = data.count(N5); + CHECK_EQUAL(1, count); + + count = data.count(N12); + CHECK_EQUAL(0, count); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_equal) + { + const DataNDC initial1(initial_data.begin(), initial_data.end()); + const DataNDC initial2(initial_data.begin(), initial_data.end()); + + CHECK(initial1 == initial2); + + const DataNDC different(different_data.begin(), different_data.end()); + + CHECK(!(initial1 == different)); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_not_equal) + { + const DataNDC initial1(initial_data.begin(), initial_data.end()); + const DataNDC initial2(initial_data.begin(), initial_data.end()); + + CHECK(!(initial1 != initial2)); + + const DataNDC different(different_data.begin(), different_data.end()); + + CHECK(initial1 != different); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_hash_function) + { + DataNDC data; + DataNDC::hasher hash_function = data.hash_function(); + + CHECK_EQUAL(simple_hash()(std::string("ABCDEF")), hash_function(std::string("ABCDEF"))); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_key_eq_function) + { + DataNDC data; + DataNDC::key_equal key_eq = data.key_eq(); + + CHECK(key_eq(std::string("ABCDEF"), std::string("ABCDEF"))); + CHECK(!key_eq(std::string("ABCDEF"), std::string("ABCDEG"))); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_load_factor) + { + // Empty. + DataNDC data; + CHECK_CLOSE(0.0, data.load_factor(), 0.01); + + // Half the buckets used. + data.assign(initial_data.begin(), initial_data.begin() + (initial_data.size() / 2)); + CHECK_CLOSE(0.5, data.load_factor(), 0.01); + + // All of the buckets used. + data.clear(); + data.assign(initial_data.begin(), initial_data.end()); + CHECK_CLOSE(1.0, data.load_factor(), 0.01); + } + }; +} diff --git a/unordered_set.h b/unordered_set.h new file mode 100644 index 00000000..e9171566 --- /dev/null +++ b/unordered_set.h @@ -0,0 +1,123 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2016 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_UNORDERED_SET__ +#define __ETL_UNORDERED_SET__ + +#include +#include +#include + +#include "iunordered_set.h" +#include "container.h" +#include "pool.h" +#include "vector.h" +#include "intrusive_forward_list.h" +#include "hash.h" + +//***************************************************************************** +///\defgroup unordered_set unordered_set +/// A unordered_set with the capacity defined at compile time. +///\ingroup containers +//***************************************************************************** + +namespace etl +{ + //************************************************************************* + /// A templated unordered_set implementation that uses a fixed size buffer. + //************************************************************************* + template , typename TKeyEqual = std::equal_to > + class unordered_set : public iunordered_set + { + private: + + typedef iunordered_set base; + + public: + + static const size_t MAX_SIZE = MAX_SIZE_; + + //************************************************************************* + /// Default constructor. + //************************************************************************* + unordered_set() + : base(node_pool, buckets) + { + base::initialise(); + } + + //************************************************************************* + /// Copy constructor. + //************************************************************************* + unordered_set(const unordered_set& other) + : base(node_pool, buckets) + { + base::assign(other.cbegin(), other.cend()); + } + + //************************************************************************* + /// Constructor, from an iterator range. + ///\tparam TIterator The iterator type. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + //************************************************************************* + template + unordered_set(TIterator first, TIterator last) + : base(node_pool, buckets) + { + base::assign(first, last); + } + + //************************************************************************* + /// Assignment operator. + //************************************************************************* + unordered_set& operator = (const unordered_set& rhs) + { + // Skip if doing self assignment + if (this != &rhs) + { + base::assign(rhs.cbegin(), rhs.cend()); + } + + return *this; + } + + private: + + /// The pool of nodes used for the unordered_set. + etl::pool node_pool; + + /// The buckets of node lists. + etl::vector, MAX_SIZE> buckets; + }; + +} + +#endif From 298f197607184451f1bb25fba832258ca91eb736 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 18 Feb 2016 20:17:58 +0000 Subject: [PATCH 4/6] Renamed internal structures --- ilist.h | 108 ++++++++++++++++++++++---------------------- list.h | 2 +- private/list_base.h | 24 +++++----- 3 files changed, 67 insertions(+), 67 deletions(-) diff --git a/ilist.h b/ilist.h index 160eb3d8..7a02f036 100644 --- a/ilist.h +++ b/ilist.h @@ -72,9 +72,9 @@ namespace etl //************************************************************************* /// The data node element in the list. //************************************************************************* - struct Data_Node : public Node + struct data_node_t : public node_t { - explicit Data_Node(parameter_t value) + explicit data_node_t(parameter_t value) : value(value) { } @@ -85,38 +85,38 @@ namespace etl private: /// The pool of data nodes used in the list. - etl::ipool* p_node_pool; + etl::ipool* p_node_pool; //************************************************************************* - /// Downcast a Node* to a Data_Node* + /// Downcast a node_t* to a data_node_t* //************************************************************************* - static Data_Node* data_cast(Node* p_node) + static data_node_t* data_cast(node_t* p_node) { - return static_cast(p_node); + return static_cast(p_node); } //************************************************************************* - /// Downcast a Node& to a Data_Node& + /// Downcast a node_t& to a data_node_t& //************************************************************************* - static Data_Node& data_cast(Node& node) + static data_node_t& data_cast(node_t& node) { - return static_cast(node); + return static_cast(node); } //************************************************************************* - /// Downcast a const Node* to a const Data_Node* + /// Downcast a const node_t* to a const data_node_t* //************************************************************************* - static const Data_Node* data_cast(const Node* p_node) + static const data_node_t* data_cast(const node_t* p_node) { - return static_cast(p_node); + return static_cast(p_node); } //************************************************************************* - /// Downcast a const Node& to a const Data_Node& + /// Downcast a const node_t& to a const data_node_t& //************************************************************************* - static const Data_Node& data_cast(const Node& node) + static const data_node_t& data_cast(const node_t& node) { - return static_cast(node); + return static_cast(node); } public: @@ -135,7 +135,7 @@ namespace etl { } - iterator(Node& node) + iterator(node_t& node) : p_node(&node) { } @@ -219,7 +219,7 @@ namespace etl private: - Node* p_node; + node_t* p_node; }; //************************************************************************* @@ -236,12 +236,12 @@ namespace etl { } - const_iterator(Node& node) + const_iterator(node_t& node) : p_node(&node) { } - const_iterator(const Node& node) + const_iterator(const node_t& node) : p_node(&node) { } @@ -298,7 +298,7 @@ namespace etl return ilist::data_cast(p_node)->value; } - const Data_Node* operator ->() const + const data_node_t* operator ->() const { return p_node; } @@ -315,7 +315,7 @@ namespace etl private: - const Node* p_node; + const node_t* p_node; }; typedef typename std::iterator_traits::difference_type difference_type; @@ -352,7 +352,7 @@ namespace etl //************************************************************************* const_iterator end() const { - return const_iterator(static_cast(terminal_node)); + return const_iterator(static_cast(terminal_node)); } //************************************************************************* @@ -368,7 +368,7 @@ namespace etl //************************************************************************* const_iterator cend() const { - return const_iterator(static_cast(terminal_node)); + return const_iterator(static_cast(terminal_node)); } //************************************************************************* @@ -384,7 +384,7 @@ namespace etl //************************************************************************* const_reverse_iterator rbegin() const { - return const_reverse_iterator(static_cast(terminal_node)); + return const_reverse_iterator(static_cast(terminal_node)); } //************************************************************************* @@ -400,7 +400,7 @@ namespace etl //************************************************************************* const_reverse_iterator crbegin() const { - return const_reverse_iterator(static_cast(terminal_node)); + return const_reverse_iterator(static_cast(terminal_node)); } //************************************************************************* @@ -461,7 +461,7 @@ namespace etl // Add all of the elements. while (first != last) { - Data_Node& data_node = allocate_data_node(*first); + data_node_t& data_node = allocate_data_node(*first); join(get_tail(), data_node); join(data_node, terminal_node); ++first; @@ -483,7 +483,7 @@ namespace etl // Add all of the elements. while (current_size < n) { - Data_Node& data_node = allocate_data_node(value); + data_node_t& data_node = allocate_data_node(value); join(*terminal_node.previous, data_node); join(data_node, terminal_node); ++current_size; @@ -498,7 +498,7 @@ namespace etl #if defined(ETL_CHECK_PUSH_POP) ETL_ASSERT(!full(), ETL_ERROR(list_full)); #endif - Data_Node& data_node = allocate_data_node(T()); + data_node_t& data_node = allocate_data_node(T()); insert_node(get_head(), data_node); } @@ -510,7 +510,7 @@ namespace etl #if defined(ETL_CHECK_PUSH_POP) ETL_ASSERT(!full(), ETL_ERROR(list_full)); #endif - Node& data_node = allocate_data_node(value); + node_t& data_node = allocate_data_node(value); insert_node(get_head(), data_node); } @@ -522,7 +522,7 @@ namespace etl #if defined(ETL_CHECK_PUSH_POP) ETL_ASSERT(!empty(), ETL_ERROR(list_empty)); #endif - Node& node = get_head(); + node_t& node = get_head(); remove_node(node); } @@ -534,7 +534,7 @@ namespace etl #if defined(ETL_CHECK_PUSH_POP) ETL_ASSERT(!full(), ETL_ERROR(list_full)); #endif - Data_Node& data_node = allocate_data_node(T()); + data_node_t& data_node = allocate_data_node(T()); insert_node(terminal_node, data_node); } @@ -546,7 +546,7 @@ namespace etl #if defined(ETL_CHECK_PUSH_POP) ETL_ASSERT(!full(), ETL_ERROR(list_full)); #endif - Data_Node& data_node = allocate_data_node(value); + data_node_t& data_node = allocate_data_node(value); insert_node(terminal_node, data_node); } @@ -558,7 +558,7 @@ namespace etl #if defined(ETL_CHECK_PUSH_POP) ETL_ASSERT(!empty(), ETL_ERROR(list_empty)); #endif - Node& node = get_tail(); + node_t& node = get_tail(); remove_node(node); } @@ -569,7 +569,7 @@ namespace etl { ETL_ASSERT(!full(), ETL_ERROR(list_full)); - Data_Node& data_node = allocate_data_node(value); + data_node_t& data_node = allocate_data_node(value); insert_node(*position.p_node, data_node); return iterator(data_node); @@ -585,7 +585,7 @@ namespace etl ETL_ASSERT(!full(), ETL_ERROR(list_full)); // Set up the next free node and insert. - Data_Node& data_node = allocate_data_node(value); + data_node_t& data_node = allocate_data_node(value); insert_node(*position.p_node, data_node); } } @@ -601,7 +601,7 @@ namespace etl ETL_ASSERT(!full(), ETL_ERROR(list_full)); // Set up the next free node and insert. - Data_Node& data_node = allocate_data_node(*first++); + data_node_t& data_node = allocate_data_node(*first++); insert_node(*position.p_node, data_node); } } @@ -624,9 +624,9 @@ namespace etl //************************************************************************* iterator erase(iterator first, iterator last) { - Node* p_first = first.p_node; - Node* p_last = last.p_node; - Node* p_next; + node_t* p_first = first.p_node; + node_t* p_last = last.p_node; + node_t* p_next; // Join the ends. join(*(p_first->previous), *p_last); @@ -638,7 +638,7 @@ namespace etl --current_size; p_next = p_first->next; // Remember the next node. - destroy_data_node(static_cast(*p_first)); // Destroy the current node. + destroy_data_node(static_cast(*p_first)); // Destroy the current node. p_first = p_next; // Move to the next node. } @@ -773,8 +773,8 @@ namespace etl return; // Can't more to before yourself! } - Node& from_node = const_cast(*from.p_node); // We're not changing the value, just it's position. - Node& to_node = const_cast(*to.p_node); // We're not changing the value, just it's position. + node_t& from_node = const_cast(*from.p_node); // We're not changing the value, just it's position. + node_t& to_node = const_cast(*to.p_node); // We're not changing the value, just it's position. // Disconnect the node from the list. join(*from_node.previous, *from_node.next); @@ -803,10 +803,10 @@ namespace etl } #endif - Node& first_node = const_cast(*first.p_node); // We're not changing the value, just it's position. - Node& last_node = const_cast(*last.p_node); // We're not changing the value, just it's position. - Node& to_node = const_cast(*to.p_node); // We're not changing the value, just it's position. - Node& final_node = *last_node.previous; + node_t& first_node = const_cast(*first.p_node); // We're not changing the value, just it's position. + node_t& last_node = const_cast(*last.p_node); // We're not changing the value, just it's position. + node_t& to_node = const_cast(*to.p_node); // We're not changing the value, just it's position. + node_t& final_node = *last_node.previous; // Disconnect the range from the list. join(*first_node.previous, last_node); @@ -956,7 +956,7 @@ namespace etl //************************************************************************* /// Constructor. //************************************************************************* - ilist(etl::ipool& node_pool, size_t max_size_) + ilist(etl::ipool& node_pool, size_t max_size_) : list_base(max_size_), p_node_pool(&node_pool) { @@ -981,30 +981,30 @@ namespace etl //************************************************************************* /// Remove a node. //************************************************************************* - void remove_node(Node& node) + void remove_node(node_t& node) { // Disconnect the node from the list. join(*node.previous, *node.next); // Destroy the pool object. - destroy_data_node(static_cast(node)); + destroy_data_node(static_cast(node)); // One less. --current_size; } //************************************************************************* - /// Allocate a Data_Node. + /// Allocate a data_node_t. //************************************************************************* - Data_Node& allocate_data_node(parameter_t value) const + data_node_t& allocate_data_node(parameter_t value) const { - return *(p_node_pool->allocate(Data_Node(value))); + return *(p_node_pool->allocate(data_node_t(value))); } //************************************************************************* - /// Destroy a Data_Node. + /// Destroy a data_node_t. //************************************************************************* - void destroy_data_node(Data_Node& node) const + void destroy_data_node(data_node_t& node) const { p_node_pool->release(&node); } diff --git a/list.h b/list.h index d192ab6d..28a52426 100644 --- a/list.h +++ b/list.h @@ -130,7 +130,7 @@ namespace etl private: /// The pool of nodes used in the list. - etl::pool node_pool; + etl::pool node_pool; }; } diff --git a/private/list_base.h b/private/list_base.h index 56c1bf8e..35d2583b 100644 --- a/private/list_base.h +++ b/private/list_base.h @@ -113,12 +113,12 @@ namespace etl //************************************************************************* /// The node element in the list. //************************************************************************* - struct Node + struct node_t { //*********************************************************************** /// Constructor //*********************************************************************** - Node() + node_t() : previous(nullptr), next(nullptr) { @@ -132,8 +132,8 @@ namespace etl std::swap(previous, next); } - Node* previous; - Node* next; + node_t* previous; + node_t* next; }; //************************************************************************* @@ -146,7 +146,7 @@ namespace etl return; } - Node* p_node = terminal_node.next; + node_t* p_node = terminal_node.next; while (p_node != &terminal_node) { @@ -204,7 +204,7 @@ namespace etl //************************************************************************* /// Get the head node. //************************************************************************* - Node& get_head() + node_t& get_head() { return *terminal_node.next; } @@ -212,7 +212,7 @@ namespace etl //************************************************************************* /// Get the head node. //************************************************************************* - const Node& get_head() const + const node_t& get_head() const { return *terminal_node.next; } @@ -220,7 +220,7 @@ namespace etl //************************************************************************* /// Get the tail node. //************************************************************************* - Node& get_tail() + node_t& get_tail() { return *terminal_node.previous; } @@ -228,7 +228,7 @@ namespace etl //************************************************************************* /// Get the tail node. //************************************************************************* - const Node& get_tail() const + const node_t& get_tail() const { return *terminal_node.previous; } @@ -236,7 +236,7 @@ namespace etl //************************************************************************* /// Insert a node before 'position'. //************************************************************************* - void insert_node(Node& position, Node& node) + void insert_node(node_t& position, node_t& node) { // Connect to the list. join(*position.previous, node); @@ -257,7 +257,7 @@ namespace etl //************************************************************************* /// Join two nodes. //************************************************************************* - void join(Node& left, Node& right) + void join(node_t& left, node_t& right) { left.next = &right; right.previous = &left; @@ -274,7 +274,7 @@ namespace etl } - Node terminal_node; ///< The node that acts as the list start and end. + node_t terminal_node; ///< The node that acts as the list start and end. size_type current_size; ///< The number of the used nodes. const size_type MAX_SIZE; ///< The maximum size of the list. }; From 3e283a5d33bd03fa1155452be58b183534706ee7 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sun, 21 Feb 2016 08:57:38 +0000 Subject: [PATCH 5/6] Added 'splice' nad 'merge' member functions. Made 'move' member functions private. --- ilist.h | 176 +++++++++++++---- private/list_base.h | 14 ++ test/data.h | 10 + test/test_list.cpp | 468 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 617 insertions(+), 51 deletions(-) diff --git a/ilist.h b/ilist.h index 7a02f036..8dc3dbc9 100644 --- a/ilist.h +++ b/ilist.h @@ -763,57 +763,113 @@ namespace etl } //************************************************************************* - /// Moves an element from one position to another within the list. - /// Moves the element at position 'from' to the position before 'to'. + /// Splices from another list to this. //************************************************************************* - void move(const_iterator from, const_iterator to) + void splice(iterator to, ilist& other) { - if (from == to) + if (&other != this) { - return; // Can't more to before yourself! + insert(to, other.begin(), other.end()); + other.erase(other.begin(), other.end()); } - - node_t& from_node = const_cast(*from.p_node); // We're not changing the value, just it's position. - node_t& to_node = const_cast(*to.p_node); // We're not changing the value, just it's position. - - // Disconnect the node from the list. - join(*from_node.previous, *from_node.next); - - // Attach it to the new position. - join(*to_node.previous, from_node); - join(from_node, to_node); } //************************************************************************* - /// Moves a range from one position to another within the list. - /// Moves a range at position 'first'/'last' to the position before 'to'. + /// Splices an element from another list to this. //************************************************************************* - void move(const_iterator first, const_iterator last, const_iterator to) + void splice(iterator to, ilist& other, iterator from) { - if ((first == to) || (last == to)) + if (&other == this) { - return; // Can't more to before yourself! + if (from != to) + { + // Internal move. + move(to, from); + } } + else + { + // From another list. + insert(to, *from); + other.erase(from); + } + } -#ifdef _DEBUG - // Check that we are not doing an illegal move! - for (const_iterator item = first; item != last; ++item) + //************************************************************************* + /// Splices a range of elements from another list to this. + //************************************************************************* + void splice(iterator to, ilist& other, iterator first, iterator last) + { + if (&other == this) { - ETL_ASSERT(item != to, ETL_ERROR(list_iterator)); + if (first != to) + { + // Internal move. + move(to, first, last); + } } + else + { + // From another list. + insert(to, first, last); + other.erase(first, last); + } + } + + //************************************************************************* + /// Merge another list into this one. Both lists should be sorted. + //************************************************************************* + void merge(ilist& other) + { + merge(other, std::less()); + } + + //************************************************************************* + /// Merge another list into this one. Both lists should be sorted. + //************************************************************************* + template + void merge(ilist& other, TCompare compare) + { + if (!other.empty()) + { +#if _DEBUG + ETL_ASSERT(etl::is_sorted(other.begin(), other.end(), compare), ETL_ERROR(list_unsorted)); + ETL_ASSERT(etl::is_sorted(begin(), end(), compare), ETL_ERROR(list_unsorted)); #endif - node_t& first_node = const_cast(*first.p_node); // We're not changing the value, just it's position. - node_t& last_node = const_cast(*last.p_node); // We're not changing the value, just it's position. - node_t& to_node = const_cast(*to.p_node); // We're not changing the value, just it's position. - node_t& final_node = *last_node.previous; + ilist::iterator other_begin = other.begin(); + ilist::iterator other_end = other.end(); - // Disconnect the range from the list. - join(*first_node.previous, last_node); + ilist::iterator this_begin = begin(); + ilist::iterator this_end = end(); - // Attach it to the new position. - join(*to_node.previous, first_node); - join(final_node, to_node); + while ((this_begin != this_end) && (other_begin != other_end)) + { + // Find the place to insert. + while ((this_begin != this_end) && !(compare(*other_begin, *this_begin))) + { + ++this_begin; + } + + // Insert. + if (this_begin != this_end) + { + while ((other_begin != other_end) && (compare(*other_begin, *this_begin))) + { + insert(this_begin, *other_begin); + ++other_begin; + } + } + } + + // Any left over? + if ((this_begin == this_end) && (other_begin != other_end)) + { + insert(this_end, other_begin, other_end); + } + + other.clear(); + } } //************************************************************************* @@ -978,6 +1034,60 @@ namespace etl private: + //************************************************************************* + /// Moves an element from one position to another within the list. + /// Moves the element at position 'from' to the position before 'to'. + //************************************************************************* + void move(iterator to, iterator from) + { + if (from == to) + { + return; // Can't more to before yourself! + } + + node_t& from_node = *from.p_node; + node_t& to_node = *to.p_node; + + // Disconnect the node from the list. + join(*from_node.previous, *from_node.next); + + // Attach it to the new position. + join(*to_node.previous, from_node); + join(from_node, to_node); + } + + //************************************************************************* + /// Moves a range from one position to another within the list. + /// Moves a range at position 'first'/'last' to the position before 'to'. + //************************************************************************* + void move(iterator to, iterator first, iterator last) + { + if ((first == to) || (last == to)) + { + return; // Can't more to before yourself! + } + +#ifdef _DEBUG + // Check that we are not doing an illegal move! + for (const_iterator item = first; item != last; ++item) + { + ETL_ASSERT(item != to, ETL_ERROR(list_iterator)); + } +#endif + + node_t& first_node = *first.p_node; + node_t& last_node = *last.p_node; + node_t& to_node = *to.p_node; + node_t& final_node = *last_node.previous; + + // Disconnect the range from the list. + join(*first_node.previous, last_node); + + // Attach it to the new position. + join(*to_node.previous, first_node); + join(final_node, to_node); + } + //************************************************************************* /// Remove a node. //************************************************************************* diff --git a/private/list_base.h b/private/list_base.h index 35d2583b..1b363a4b 100644 --- a/private/list_base.h +++ b/private/list_base.h @@ -100,6 +100,20 @@ namespace etl } }; + //*************************************************************************** + /// Unsorted exception for the list. + ///\ingroup list + //*************************************************************************** + class list_unsorted : public list_exception + { + public: + + list_unsorted(string_type file_name, numeric_type line_number) + : list_exception(ETL_ERROR_TEXT("list:unsorted", ETL_FILE"D"), file_name, line_number) + { + } + }; + //*************************************************************************** /// The base class for all lists. ///\ingroup list diff --git a/test/data.h b/test/data.h index 22c46381..c256cc4f 100644 --- a/test/data.h +++ b/test/data.h @@ -54,6 +54,11 @@ public: return value < other.value; } + bool operator > (const TestDataDC& other) const + { + return value > other.value; + } + T value; }; @@ -97,6 +102,11 @@ public: return value < other.value; } + bool operator > (const TestDataNDC& other) const + { + return value > other.value; + } + T value; }; diff --git a/test/test_list.cpp b/test/test_list.cpp index d3825c82..4a92c29d 100644 --- a/test/test_list.cpp +++ b/test/test_list.cpp @@ -47,9 +47,10 @@ namespace const size_t SIZE = 10; - typedef etl::list DataDC; - typedef etl::list DataNDC; - typedef etl::ilist IDataNDC; + typedef etl::list DataDC; + typedef etl::list DataNDC; + typedef etl::list DataNDC2; + typedef etl::ilist IDataNDC; typedef std::list CompareData; typedef std::vector InitialData; @@ -59,6 +60,12 @@ namespace InitialData non_unique_data; InitialData small_data; + InitialData merge_data0; + InitialData merge_data1; + InitialData merge_data2; + InitialData merge_data3; + InitialData merge_data4; + bool are_equal; //************************************************************************* @@ -70,6 +77,12 @@ namespace sorted_data = { ItemNDC("0"), ItemNDC("1"), ItemNDC("2"), ItemNDC("3"), ItemNDC("4"), ItemNDC("5"), ItemNDC("6"), ItemNDC("7"), ItemNDC("8"), ItemNDC("9") }; non_unique_data = { ItemNDC("0"), ItemNDC("0"), ItemNDC("1"), ItemNDC("1"), ItemNDC("2"), ItemNDC("3"), ItemNDC("3"), ItemNDC("3"), ItemNDC("4"), ItemNDC("5") }; small_data = { ItemNDC("0"), ItemNDC("1"), ItemNDC("2"), ItemNDC("3"), ItemNDC("4"), ItemNDC("5") }; + + merge_data0 = { ItemNDC("1"), ItemNDC("1"), ItemNDC("3"), ItemNDC("3"), ItemNDC("5"), ItemNDC("7"), ItemNDC("8") }; + merge_data1 = { ItemNDC("1"), ItemNDC("2"), ItemNDC("3"), ItemNDC("3"), ItemNDC("6"), ItemNDC("9"), ItemNDC("9") }; + merge_data2 = { ItemNDC("0"), ItemNDC("2"), ItemNDC("3"), ItemNDC("3"), ItemNDC("6"), ItemNDC("7"), ItemNDC("7") }; + merge_data3 = { ItemNDC("0"), ItemNDC("2"), ItemNDC("3"), ItemNDC("3"), ItemNDC("6"), ItemNDC("7") }; + merge_data4 = { ItemNDC("0"), ItemNDC("2"), ItemNDC("3"), ItemNDC("3"), ItemNDC("6"), ItemNDC("7"), ItemNDC("8"), ItemNDC("9") }; } }; @@ -808,7 +821,7 @@ namespace } //************************************************************************* - TEST_FIXTURE(SetupFixture, test_move) + TEST_FIXTURE(SetupFixture, test_splice_same) { CompareData compare_data(unsorted_data.begin(), unsorted_data.end()); DataNDC data(unsorted_data.begin(), unsorted_data.end()); @@ -816,8 +829,8 @@ namespace CompareData::iterator compare_from; CompareData::iterator compare_to; - DataNDC::const_iterator from; - DataNDC::const_iterator to; + DataNDC::iterator from; + DataNDC::iterator to; // Move to the beginning. compare_from = compare_data.begin(); @@ -828,7 +841,7 @@ namespace from = data.begin(); std::advance(from, 4); to = data.begin(); - data.move(from, to); + data.splice(to, data, from); are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); CHECK(are_equal); @@ -842,7 +855,7 @@ namespace from = data.begin(); std::advance(from, 4); to = data.end(); - data.move(from, to); + data.splice(to, data, from); are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); CHECK(are_equal); @@ -858,7 +871,7 @@ namespace std::advance(from, 4); to = data.begin(); std::advance(to, 6); - data.move(from, to); + data.splice(to, data, from); are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); CHECK(are_equal); @@ -874,14 +887,120 @@ namespace std::advance(from, 4); to = data.begin(); std::advance(to, 4); - data.move(from, to); + data.splice(to, data, from); are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); CHECK(are_equal); } //************************************************************************* - TEST_FIXTURE(SetupFixture, test_move_range) + TEST_FIXTURE(SetupFixture, test_splice_different) + { + CompareData compare_data(unsorted_data.begin(), unsorted_data.end()); + CompareData compare_data2(unsorted_data.begin(), unsorted_data.end()); + + DataNDC2 data(unsorted_data.begin(), unsorted_data.end()); + DataNDC2 data2(unsorted_data.begin(), unsorted_data.end()); + + CompareData::iterator compare_from; + CompareData::iterator compare_to; + + DataNDC2::iterator from; + DataNDC2::iterator to; + + // Move to the beginning. + compare_from = compare_data2.begin(); + std::advance(compare_from, 4); + compare_to = compare_data.begin(); + compare_data.splice(compare_to, compare_data2, compare_from); + + from = data2.begin(); + std::advance(from, 4); + to = data.begin(); + data.splice(to, data2, from); + + are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); + CHECK(are_equal); + + are_equal = std::equal(data2.begin(), data2.end(), compare_data2.begin()); + CHECK(are_equal); + + // Move to the end. + compare_data.assign(unsorted_data.begin(), unsorted_data.end()); + compare_data2.assign(unsorted_data.begin(), unsorted_data.end()); + + data.assign(unsorted_data.begin(), unsorted_data.end()); + data2.assign(unsorted_data.begin(), unsorted_data.end()); + + compare_from = compare_data2.begin(); + std::advance(compare_from, 4); + compare_to = compare_data.end(); + compare_data.splice(compare_to, compare_data2, compare_from); + + from = data2.begin(); + std::advance(from, 4); + to = data.end(); + data.splice(to, data2, from); + + are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); + CHECK(are_equal); + + are_equal = std::equal(data2.begin(), data2.end(), compare_data2.begin()); + CHECK(are_equal); + + // Move nearby. + compare_data.assign(unsorted_data.begin(), unsorted_data.end()); + compare_data2.assign(unsorted_data.begin(), unsorted_data.end()); + + data.assign(unsorted_data.begin(), unsorted_data.end()); + data2.assign(unsorted_data.begin(), unsorted_data.end()); + + compare_from = compare_data2.begin(); + std::advance(compare_from, 4); + compare_to = compare_data.begin(); + std::advance(compare_to, 6); + compare_data.splice(compare_to, compare_data2, compare_from); + + from = data2.begin(); + std::advance(from, 4); + to = data.begin(); + std::advance(to, 6); + data.splice(to, data2, from); + + are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); + CHECK(are_equal); + + are_equal = std::equal(data2.begin(), data2.end(), compare_data2.begin()); + CHECK(are_equal); + + // Move to same place. + compare_data.assign(unsorted_data.begin(), unsorted_data.end()); + compare_data2.assign(unsorted_data.begin(), unsorted_data.end()); + + data.assign(unsorted_data.begin(), unsorted_data.end()); + data2.assign(unsorted_data.begin(), unsorted_data.end()); + + compare_from = compare_data2.begin(); + std::advance(compare_from, 4); + compare_to = compare_data.begin(); + std::advance(compare_to, 4); + compare_data.splice(compare_to, compare_data2, compare_from); + + from = data2.begin(); + std::advance(from, 4); + to = data.begin(); + std::advance(to, 4); + data.splice(to, data2, from); + + are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); + CHECK(are_equal); + + are_equal = std::equal(data2.begin(), data2.end(), compare_data2.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_splice_range_same) { CompareData compare_data(unsorted_data.begin(), unsorted_data.end()); DataNDC data(unsorted_data.begin(), unsorted_data.end()); @@ -890,9 +1009,9 @@ namespace CompareData::iterator compare_end; CompareData::iterator compare_to; - DataNDC::const_iterator begin; - DataNDC::const_iterator end; - DataNDC::const_iterator to; + DataNDC::iterator begin; + DataNDC::iterator end; + DataNDC::iterator to; // Move to the beginning. compare_begin = compare_data.begin(); @@ -907,7 +1026,7 @@ namespace end = begin; std::advance(end, 3); to = data.begin(); - data.move(begin, end, to); + data.splice(to, data, begin, end); are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); CHECK(are_equal); @@ -925,7 +1044,7 @@ namespace end = begin; std::advance(end, 3); to = data.end(); - data.move(begin, end, to); + data.splice(to, data, begin, end); are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); CHECK(are_equal); @@ -945,7 +1064,7 @@ namespace std::advance(end, 3); to = data.begin(); std::advance(to, 7); - data.move(begin, end, to); + data.splice(to, data, begin, end); are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); CHECK(are_equal); @@ -958,7 +1077,320 @@ namespace to = data.begin(); std::advance(to, 4); - CHECK_THROW(data.move(begin, end, to), etl::list_iterator); + CHECK_THROW(data.splice(to, data, begin, end), etl::list_iterator); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_splice_range_different) + { + CompareData compare_data(unsorted_data.begin(), unsorted_data.end()); + CompareData compare_data2(unsorted_data.begin(), unsorted_data.end()); + DataNDC2 data(unsorted_data.begin(), unsorted_data.end()); + DataNDC2 data2(unsorted_data.begin(), unsorted_data.end()); + + CompareData::iterator compare_begin; + CompareData::iterator compare_end; + CompareData::iterator compare_to; + + DataNDC2::iterator begin; + DataNDC2::iterator end; + DataNDC2::iterator to; + + // Move to the beginning. + compare_begin = compare_data2.begin(); + std::advance(compare_begin, 3); + compare_end = compare_begin; + std::advance(compare_end, 3); + compare_to = compare_data.begin(); + compare_data.splice(compare_to, compare_data2, compare_begin, compare_end); + + begin = data2.begin(); + std::advance(begin, 3); + end = begin; + std::advance(end, 3); + to = data.begin(); + data.splice(to, data2, begin, end); + + are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); + CHECK(are_equal); + + are_equal = std::equal(data2.begin(), data2.end(), compare_data2.begin()); + CHECK(are_equal); + + // Move to the end. + compare_data.assign(unsorted_data.begin(), unsorted_data.end()); + compare_data2.assign(unsorted_data.begin(), unsorted_data.end()); + + data.assign(unsorted_data.begin(), unsorted_data.end()); + data2.assign(unsorted_data.begin(), unsorted_data.end()); + + compare_begin = compare_data2.begin(); + std::advance(compare_begin, 3); + compare_end = compare_begin; + std::advance(compare_end, 3); + compare_to = compare_data.end(); + compare_data.splice(compare_to, compare_data2, compare_begin, compare_end); + + begin = data2.begin(); + std::advance(begin, 3); + end = begin; + std::advance(end, 3); + to = data.end(); + data.splice(to, data2, begin, end); + + are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); + CHECK(are_equal); + + are_equal = std::equal(data2.begin(), data2.end(), compare_data2.begin()); + CHECK(are_equal); + + // Move nearby. + compare_data.assign(unsorted_data.begin(), unsorted_data.end()); + compare_data2.assign(unsorted_data.begin(), unsorted_data.end()); + + data.assign(unsorted_data.begin(), unsorted_data.end()); + data2.assign(unsorted_data.begin(), unsorted_data.end()); + + compare_begin = compare_data2.begin(); + std::advance(compare_begin, 2); + compare_end = compare_begin; + std::advance(compare_end, 3); + compare_to = compare_data.begin(); + std::advance(compare_to, 7); + compare_data.splice(compare_to, compare_data2, compare_begin, compare_end); + + begin = data2.begin(); + std::advance(begin, 2); + end = begin; + std::advance(end, 3); + to = data.begin(); + std::advance(to, 7); + data.splice(to, data2, begin, end); + + are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); + CHECK(are_equal); + + are_equal = std::equal(data2.begin(), data2.end(), compare_data2.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_splice_list_same) + { + CompareData compare_data(unsorted_data.begin(), unsorted_data.end()); + DataNDC data(unsorted_data.begin(), unsorted_data.end()); + + CompareData::iterator compare_to; + DataNDC::iterator to; + + // Move to the beginning. + compare_to = compare_data.begin(); + compare_data.splice(compare_to, compare_data); + + to = data.begin(); + data.splice(to, data); + + are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); + CHECK(are_equal); + + // Move to the end. + compare_to = compare_data.end(); + compare_data.splice(compare_to, compare_data); + + to = data.end(); + data.splice(to, data); + + are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); + CHECK(are_equal); + + // Move nearby. + compare_to = compare_data.begin(); + std::advance(compare_to, 7); + compare_data.splice(compare_to, compare_data); + + to = data.begin(); + std::advance(to, 7); + data.splice(to, data); + + are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_splice_list_different) + { + CompareData compare_data(unsorted_data.begin(), unsorted_data.end()); + CompareData compare_data2(unsorted_data.begin(), unsorted_data.end()); + + DataNDC2 data(unsorted_data.begin(), unsorted_data.end()); + DataNDC2 data2(unsorted_data.begin(), unsorted_data.end()); + + CompareData::iterator compare_to; + DataNDC2::iterator to; + + // Move to the beginning. + compare_to = compare_data.begin(); + compare_data.splice(compare_to, compare_data2); + + to = data.begin(); + data.splice(to, data2); + + are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); + CHECK(are_equal); + + are_equal = std::equal(data2.begin(), data2.end(), compare_data2.begin()); + CHECK(are_equal); + + // Move to the end. + compare_data.assign(unsorted_data.begin(), unsorted_data.end()); + compare_data2.assign(unsorted_data.begin(), unsorted_data.end()); + + data.assign(unsorted_data.begin(), unsorted_data.end()); + data2.assign(unsorted_data.begin(), unsorted_data.end()); + + compare_to = compare_data.end(); + compare_data.splice(compare_to, compare_data2); + + to = data.end(); + data.splice(to, data2); + + are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); + CHECK(are_equal); + + are_equal = std::equal(data2.begin(), data2.end(), compare_data2.begin()); + CHECK(are_equal); + + // Move nearby. + compare_data.assign(unsorted_data.begin(), unsorted_data.end()); + compare_data2.assign(unsorted_data.begin(), unsorted_data.end()); + + data.assign(unsorted_data.begin(), unsorted_data.end()); + data2.assign(unsorted_data.begin(), unsorted_data.end()); + + compare_to = compare_data.begin(); + std::advance(compare_to, 7); + compare_data.splice(compare_to, compare_data2); + + to = data.begin(); + std::advance(to, 7); + data.splice(to, data2); + + are_equal = std::equal(data.begin(), data.end(), compare_data.begin()); + CHECK(are_equal); + + are_equal = std::equal(data2.begin(), data2.end(), compare_data2.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_merge_0_1) + { + bool are_equal; + + DataNDC2 data0(merge_data0.begin(), merge_data0.end()); + DataNDC2 data1(merge_data1.begin(), merge_data1.end()); + + CompareData compare0(merge_data0.begin(), merge_data0.end()); + CompareData compare1(merge_data1.begin(), merge_data1.end()); + + data0.merge(data1); + compare0.merge(compare1); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(data0.size(), compare0.size()); + CHECK_EQUAL(data1.size(), compare1.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_merge_0_2) + { + bool are_equal; + + DataNDC2 data0(merge_data0.begin(), merge_data0.end()); + DataNDC2 data2(merge_data2.begin(), merge_data2.end()); + + CompareData compare0(merge_data0.begin(), merge_data0.end()); + CompareData compare2(merge_data2.begin(), merge_data2.end()); + + data0.merge(data2); + compare0.merge(compare2); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(data0.size(), compare0.size()); + CHECK_EQUAL(data2.size(), compare2.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_merge_0_3) + { + bool are_equal; + + DataNDC2 data0(merge_data0.begin(), merge_data0.end()); + DataNDC2 data3(merge_data3.begin(), merge_data3.end()); + + CompareData compare0(merge_data0.begin(), merge_data0.end()); + CompareData compare3(merge_data3.begin(), merge_data3.end()); + + data0.merge(data3); + compare0.merge(compare3); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(data0.size(), compare0.size()); + CHECK_EQUAL(data3.size(), compare3.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_merge_0_4) + { + bool are_equal; + + DataNDC2 data0(merge_data0.begin(), merge_data0.end()); + DataNDC2 data4(merge_data4.begin(), merge_data4.end()); + + CompareData compare0(merge_data0.begin(), merge_data0.end()); + CompareData compare4(merge_data4.begin(), merge_data4.end()); + + data0.merge(data4); + compare0.merge(compare4); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(data0.size(), compare0.size()); + CHECK_EQUAL(data4.size(), compare4.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_merge_0_1_reverse_order) + { + bool are_equal; + + DataNDC2 data0(merge_data0.begin(), merge_data0.end()); + DataNDC2 data1(merge_data1.begin(), merge_data1.end()); + + data0.reverse(); + data1.reverse(); + + CompareData compare0(merge_data0.begin(), merge_data0.end()); + CompareData compare1(merge_data1.begin(), merge_data1.end()); + + compare0.reverse(); + compare1.reverse(); + + data0.merge(data1, std::greater()); + compare0.merge(compare1, std::greater()); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(data0.size(), compare0.size()); + CHECK_EQUAL(data1.size(), compare1.size()); } }; } From 02aa427e6b804a81b6b1a0291c9dfa34841719fc Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sun, 21 Feb 2016 21:11:46 +0000 Subject: [PATCH 6/6] Added unordered_set, unordered_multiset, unordered_multimap --- iunordered_map.h | 42 ++ iunordered_multimap.h | 1191 ++++++++++++++++++++++++++++++ iunordered_multiset.h | 1183 +++++++++++++++++++++++++++++ iunordered_set.h | 1157 +++++++++++++++++++++++++++++ test/test_unordered_map.cpp | 52 ++ test/test_unordered_multimap.cpp | 592 +++++++++++++++ test/test_unordered_multiset.cpp | 522 +++++++++++++ test/vs2015/etl.vcxproj | 5 + test/vs2015/etl.vcxproj.filters | 15 + unordered_multimap.h | 123 +++ unordered_multiset.h | 123 +++ 11 files changed, 5005 insertions(+) create mode 100644 iunordered_multimap.h create mode 100644 iunordered_multiset.h create mode 100644 iunordered_set.h create mode 100644 test/test_unordered_multimap.cpp create mode 100644 test/test_unordered_multiset.cpp create mode 100644 unordered_multimap.h create mode 100644 unordered_multiset.h diff --git a/iunordered_map.h b/iunordered_map.h index b2a9e0d4..d4e6260a 100644 --- a/iunordered_map.h +++ b/iunordered_map.h @@ -1047,6 +1047,48 @@ namespace etl return end(); } + //********************************************************************* + /// Returns a range containing all elements with key key in the container. + /// The range is defined by two iterators, the first pointing to the first + /// element of the wanted range and the second pointing past the last + /// element of the range. + ///\param key The key to search for. + ///\return An iterator pair to the range of elements if the key exists, otherwise end(). + //********************************************************************* + std::pair equal_range(const key_value_parameter_t& key) + { + iterator first = find(key); + iterator last = first; + + if (last != end()) + { + ++last; + } + + return std::pair(first, last); + } + + //********************************************************************* + /// Returns a range containing all elements with key key in the container. + /// The range is defined by two iterators, the first pointing to the first + /// element of the wanted range and the second pointing past the last + /// element of the range. + ///\param key The key to search for. + ///\return A const iterator pair to the range of elements if the key exists, otherwise end(). + //********************************************************************* + std::pair equal_range(const key_value_parameter_t& key) const + { + const_iterator first = find(key); + const_iterator last = first; + + if (last != end()) + { + ++last; + } + + return std::pair(first, last); + } + //************************************************************************* /// Gets the size of the unordered_map. //************************************************************************* diff --git a/iunordered_multimap.h b/iunordered_multimap.h new file mode 100644 index 00000000..d6a1d320 --- /dev/null +++ b/iunordered_multimap.h @@ -0,0 +1,1191 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2016 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_IUNORDERED_MULTIMAP__ +#define __ETL_IUNORDERED_MULTIMAP__ + +#include +#include +#include +#include +#include + +#include "type_traits.h" +#include "parameter_type.h" +#include "hash.h" +#include "nullptr.h" +#include "ipool.h" +#include "ivector.h" +#include "error_handler.h" +#include "intrusive_forward_list.h" +#include "exception.h" +#include "error_handler.h" + +#undef ETL_FILE +#define ETL_FILE "16" + +namespace etl +{ + //*************************************************************************** + /// Exception for the unordered_multimap. + ///\ingroup unordered_multimap + //*************************************************************************** + class unordered_multimap_exception : public exception + { + public: + + unordered_multimap_exception(string_type what, string_type file_name, numeric_type line_number) + : exception(what, file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Full exception for the unordered_multimap. + ///\ingroup unordered_multimap + //*************************************************************************** + class unordered_multimap_full : public unordered_multimap_exception + { + public: + + unordered_multimap_full(string_type file_name, numeric_type line_number) + : unordered_multimap_exception(ETL_ERROR_TEXT("unordered_multimap:full", ETL_FILE"A"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Out of range exception for the unordered_multimap. + ///\ingroup unordered_multimap + //*************************************************************************** + class unordered_multimap_out_of_range : public unordered_multimap_exception + { + public: + + unordered_multimap_out_of_range(string_type file_name, numeric_type line_number) + : unordered_multimap_exception(ETL_ERROR_TEXT("unordered_multimap:range", ETL_FILE"B"), file_name, line_number) + {} + }; + + //*************************************************************************** + /// Iterator exception for the unordered_multimap. + ///\ingroup unordered_multimap + //*************************************************************************** + class unordered_multimap_iterator : public unordered_multimap_exception + { + public: + + unordered_multimap_iterator(string_type file_name, numeric_type line_number) + : unordered_multimap_exception(ETL_ERROR_TEXT("unordered_multimap:iterator", ETL_FILE"C"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// The base class for specifically sized unordered_multimap. + /// Can be used as a reference type for all unordered_multimap containing a specific type. + ///\ingroup unordered_multimap + //*************************************************************************** + template , typename TKeyEqual = std::equal_to > + class iunordered_multimap + { + public: + + typedef std::pair value_type; + + typedef TKey key_type; + typedef T mapped_type; + typedef THash hasher; + typedef TKeyEqual key_equal; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef size_t size_type; + + + typedef typename parameter_type::type key_value_parameter_t; + + typedef etl::forward_link<> link_t; // Default link. + + // The nodes that store the elements. + struct node_t : public link_t + { + node_t(const value_type& key_value_pair) + : key_value_pair(key_value_pair) + { + } + + value_type key_value_pair; + }; + + private: + + typedef etl::intrusive_forward_list bucket_t; + typedef etl::ipool pool_t; + typedef etl::ivector bucket_list_t; + + typedef typename bucket_list_t::iterator bucket_list_iterator; + + public: + + // Local iterators iterate over one bucket. + typedef typename bucket_t::iterator local_iterator; + typedef typename bucket_t::const_iterator local_const_iterator; + + //********************************************************************* + class iterator : public std::iterator + { + public: + + typedef typename iunordered_multimap::value_type value_type; + typedef typename iunordered_multimap::key_type key_type; + typedef typename iunordered_multimap::mapped_type mapped_type; + typedef typename iunordered_multimap::hasher hasher; + typedef typename iunordered_multimap::key_equal key_equal; + typedef typename iunordered_multimap::reference reference; + typedef typename iunordered_multimap::const_reference const_reference; + typedef typename iunordered_multimap::pointer pointer; + typedef typename iunordered_multimap::const_pointer const_pointer; + typedef typename iunordered_multimap::size_type size_type; + + friend class iunordered_multimap; + + //********************************* + iterator() + { + } + + //********************************* + iterator(const iterator& other) + : ibuckets_end(other.ibuckets_end), + ibucket(other.ibucket), + inode(other.inode) + { + } + + //********************************* + iterator& operator ++() + { + ++inode; + + // The end of this node list? + if (inode == ibucket->end()) + { + // Search for the next non-empty bucket. + ++ibucket; + while ((ibucket != ibuckets_end) && (ibucket->empty())) + { + ++ibucket; + } + + // If not past the end, get the first node in the bucket. + if (ibucket != ibuckets_end) + { + inode = ibucket->begin(); + } + } + + return *this; + } + + //********************************* + iterator operator ++(int) + { + iterator temp(*this); + operator++(); + return temp; + } + + //********************************* + iterator operator =(const iterator& other) + { + ibuckets_end = other.ibuckets_end; + ibucket = other.ibucket; + inode = other.inode; + return *this; + } + + //********************************* + std::pair operator *() + { + return inode->key_value_pair; + } + + //********************************* + const_reference operator *() const + { + return inode->key_value_pair; + } + + //********************************* + pointer operator &() + { + return &(inode->key_value_pair); + } + + //********************************* + const_pointer operator &() const + { + return &(inode->key_value_pair); + } + + //********************************* + pointer operator ->() + { + return &(inode->key_value_pair); + } + + //********************************* + const_pointer operator ->() const + { + return &(inode->key_value_pair); + } + + //********************************* + friend bool operator == (const iterator& lhs, const iterator& rhs) + { + return lhs.compare(rhs); + } + + //********************************* + friend bool operator != (const iterator& lhs, const iterator& rhs) + { + return !(lhs == rhs); + } + + private: + + //********************************* + iterator(bucket_list_iterator ibuckets_end, bucket_list_iterator ibucket, local_iterator inode) + : ibuckets_end(ibuckets_end), + ibucket(ibucket), + inode(inode) + { + } + + //********************************* + bool compare(const iterator& rhs) const + { + return rhs.inode == inode; + } + + //********************************* + bucket_t& get_bucket() + { + return *ibucket; + } + + //********************************* + bucket_list_iterator& get_bucket_list_iterator() + { + return ibucket; + } + + //********************************* + local_iterator get_local_iterator() + { + return inode; + } + + bucket_list_iterator ibuckets_end; + bucket_list_iterator ibucket; + local_iterator inode; + }; + + //********************************************************************* + class const_iterator : public std::iterator + { + public: + + typedef typename iunordered_multimap::value_type value_type; + typedef typename iunordered_multimap::key_type key_type; + typedef typename iunordered_multimap::mapped_type mapped_type; + typedef typename iunordered_multimap::hasher hasher; + typedef typename iunordered_multimap::key_equal key_equal; + typedef typename iunordered_multimap::reference reference; + typedef typename iunordered_multimap::const_reference const_reference; + typedef typename iunordered_multimap::pointer pointer; + typedef typename iunordered_multimap::const_pointer const_pointer; + typedef typename iunordered_multimap::size_type size_type; + + friend class iunordered_multimap; + friend class iterator; + + //********************************* + const_iterator() + { + } + + //********************************* + const_iterator(const typename iunordered_multimap::iterator& other) + : ibuckets_end(other.ibuckets_end), + ibucket(other.ibucket), + inode(other.inode) + { + } + + //********************************* + const_iterator(const const_iterator& other) + : ibuckets_end(other.ibuckets_end), + ibucket(other.ibucket), + inode(other.inode) + { + } + + //********************************* + const_iterator& operator ++() + { + ++inode; + + // The end of this node list? + if (inode == ibucket->end()) + { + // Search for the next non-empty bucket. + + ++ibucket; + while ((ibucket != ibuckets_end) && (ibucket->empty())) + { + ++ibucket; + } + + // If not past the end, get the first node in the bucket. + if (ibucket != ibuckets_end) + { + inode = ibucket->begin(); + } + } + + return *this; + } + + //********************************* + const_iterator operator ++(int) + { + const_iterator temp(*this); + operator++(); + return temp; + } + + //********************************* + const_iterator operator =(const const_iterator& other) + { + ibuckets_end = other.ibuckets_end; + ibucket = other.ibucket; + inode = other.inode; + return *this; + } + + //********************************* + const_reference operator *() const + { + return inode->key_value_pair; + } + + //********************************* + const_pointer operator &() const + { + return &(inode->key_value_pair); + } + + //********************************* + const_pointer operator ->() const + { + return &(inode->key_value_pair); + } + + //********************************* + friend bool operator == (const const_iterator& lhs, const const_iterator& rhs) + { + return lhs.compare(rhs); + } + + //********************************* + friend bool operator != (const const_iterator& lhs, const const_iterator& rhs) + { + return !(lhs == rhs); + } + + private: + + //********************************* + const_iterator(bucket_list_iterator ibuckets_end, bucket_list_iterator ibucket, local_iterator inode) + : ibuckets_end(ibuckets_end), + ibucket(ibucket), + inode(inode) + { + } + + //********************************* + bool compare(const const_iterator& rhs) const + { + return rhs.inode == inode; + } + + //********************************* + bucket_t& get_bucket() + { + return *ibucket; + } + + //********************************* + bucket_list_iterator& get_bucket_list_iterator() + { + return ibucket; + } + + //********************************* + local_iterator get_local_iterator() + { + return inode; + } + + bucket_list_iterator ibuckets_end; + bucket_list_iterator ibucket; + local_iterator inode; + }; + + typedef typename std::iterator_traits::difference_type difference_type; + + //********************************************************************* + /// Returns an iterator to the beginning of the unordered_multimap. + ///\return An iterator to the beginning of the unordered_multimap. + //********************************************************************* + iterator begin() + { + return iterator(pbuckets->end(), first, first->begin()); + } + + //********************************************************************* + /// Returns a const_iterator to the beginning of the unordered_multimap. + ///\return A const iterator to the beginning of the unordered_multimap. + //********************************************************************* + const_iterator begin() const + { + return const_iterator(pbuckets->end(), first, first->begin()); + } + + //********************************************************************* + /// Returns a const_iterator to the beginning of the unordered_multimap. + ///\return A const iterator to the beginning of the unordered_multimap. + //********************************************************************* + const_iterator cbegin() const + { + return const_iterator(pbuckets->end(), first, first->begin()); + } + + //********************************************************************* + /// Returns an iterator to the beginning of the unordered_multimap bucket. + ///\return An iterator to the beginning of the unordered_multimap bucket. + //********************************************************************* + local_iterator begin(size_t i) + { + return (*pbuckets)[i].begin(); + } + + //********************************************************************* + /// Returns a const_iterator to the beginning of the unordered_multimap bucket. + ///\return A const iterator to the beginning of the unordered_multimap bucket. + //********************************************************************* + local_const_iterator begin(size_t i) const + { + return (*pbuckets)[i].cbegin(); + } + + //********************************************************************* + /// Returns a const_iterator to the beginning of the unordered_multimap bucket. + ///\return A const iterator to the beginning of the unordered_multimap bucket. + //********************************************************************* + local_const_iterator cbegin(size_t i) const + { + return (*pbuckets)[i].cbegin(); + } + + //********************************************************************* + /// Returns an iterator to the end of the unordered_multimap. + ///\return An iterator to the end of the unordered_multimap. + //********************************************************************* + iterator end() + { + return iterator(pbuckets->end(), last, last->end()); + } + + //********************************************************************* + /// Returns a const_iterator to the end of the unordered_multimap. + ///\return A const iterator to the end of the unordered_multimap. + //********************************************************************* + const_iterator end() const + { + return const_iterator(pbuckets->end(), last, last->end()); + } + + //********************************************************************* + /// Returns a const_iterator to the end of the unordered_multimap. + ///\return A const iterator to the end of the unordered_multimap. + //********************************************************************* + const_iterator cend() const + { + return const_iterator(pbuckets->end(), last, last->end()); + } + + //********************************************************************* + /// Returns an iterator to the end of the unordered_multimap bucket. + ///\return An iterator to the end of the unordered_multimap bucket. + //********************************************************************* + local_iterator end(size_t i) + { + return (*pbuckets)[i].end(); + } + + //********************************************************************* + /// Returns a const_iterator to the end of the unordered_multimap bucket. + ///\return A const iterator to the end of the unordered_multimap bucket. + //********************************************************************* + local_const_iterator end(size_t i) const + { + return (*pbuckets)[i].cend(); + } + + //********************************************************************* + /// Returns a const_iterator to the end of the unordered_multimap bucket. + ///\return A const iterator to the end of the unordered_multimap bucket. + //********************************************************************* + local_const_iterator cend(size_t i) const + { + return (*pbuckets)[i].cend(); + } + + //********************************************************************* + /// Returns the bucket index for the key. + ///\return The bucket index for the key. + //********************************************************************* + size_type bucket(key_value_parameter_t key) const + { + return key_hash_function(key) % pbuckets->size(); + } + + //********************************************************************* + /// Returns the size of the bucket key. + ///\return The bucket size of the bucket key. + //********************************************************************* + size_type bucket_size(key_value_parameter_t key) const + { + size_t index = bucket(key); + + return std::distance((*pbuckets)[index].begin(), (*pbuckets)[index].end()); + } + + //********************************************************************* + /// Returns the maximum number of the buckets the container can hold. + ///\return The maximum number of the buckets the container can hold. + //********************************************************************* + size_type max_bucket_count() const + { + return max_size(); + } + + //********************************************************************* + /// Returns the number of the buckets the container holds. + ///\return The number of the buckets the container holds. + //********************************************************************* + size_type bucket_count() const + { + return max_size(); + } + + //********************************************************************* + /// Assigns values to the unordered_multimap. + /// If asserts or exceptions are enabled, emits unordered_multimap_full if the unordered_multimap does not have enough free space. + /// If asserts or exceptions are enabled, emits unordered_multimap_iterator if the iterators are reversed. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + //********************************************************************* + template + void assign(TIterator first, TIterator last) + { +#ifdef _DEBUG + difference_type count = std::distance(first, last); + ETL_ASSERT(count >= 0, ETL_ERROR(unordered_multimap_iterator)); + ETL_ASSERT(size_t(count) <= max_size() , ETL_ERROR(unordered_multimap_full)); +#endif + + clear(); + + while (first != last) + { + insert(*first++); + } + } + + //********************************************************************* + /// Inserts a value to the unordered_multimap. + /// If asserts or exceptions are enabled, emits unordered_multimap_full if the unordered_multimap is already full. + ///\param value The value to insert. + //********************************************************************* + std::pair insert(const value_type& key_value_pair) + { + std::pair result(end(), false); + + ETL_ASSERT(!full(), ETL_ERROR(unordered_multimap_full)); + + const key_type& key = key_value_pair.first; + const mapped_type& mapped = key_value_pair.second; + + // Get the hash index. + size_t index = bucket(key); + + // Get the bucket & bucket iterator. + bucket_list_iterator ibucket = pbuckets->begin() + index; + bucket_t& bucket = *ibucket; + + // The first one in the bucket? + if (bucket.empty()) + { + // Get a new node. + node_t& node = *pnodepool->allocate(node_t(key_value_pair)); + + // Just add the pointer to the bucket; + bucket.insert_after(bucket.before_begin(), node); + + result.first = iterator(pbuckets->end(), ibucket, ibucket->begin()); + result.second = true; + + adjust_first_last_markers(ibucket); + } + else + { + // Step though the bucket looking for a place to insert. + local_iterator inode_previous = bucket.before_begin(); + local_iterator inode = bucket.begin(); + + while (inode != bucket.end()) + { + // Do we already have this key? + if (inode->key_value_pair.first == key) + { + break; + } + + ++inode_previous; + ++inode; + } + + // Get a new node. + node_t& node = *pnodepool->allocate(node_t(key_value_pair)); + + // Add the node to the end of the bucket; + bucket.insert_after(inode_previous, node); + ++inode_previous; + + result.first = iterator(pbuckets->end(), ibucket, inode_previous); + result.second = true; + } + + return result; + } + + //********************************************************************* + /// Inserts a value to the unordered_multimap. + /// If asserts or exceptions are enabled, emits unordered_multimap_full if the unordered_multimap is already full. + ///\param position The position to insert at. + ///\param value The value to insert. + //********************************************************************* + iterator insert(const_iterator position, const value_type& key_value_pair) + { + return insert(key_value_pair).first; + } + + //********************************************************************* + /// Inserts a range of values to the unordered_multimap. + /// If asserts or exceptions are enabled, emits unordered_multimap_full if the unordered_multimap does not have enough free space. + ///\param position The position to insert at. + ///\param first The first element to add. + ///\param last The last + 1 element to add. + //********************************************************************* + template + void insert(TIterator first, TIterator last) + { + while (first != last) + { + insert(*first++); + } + } + + //********************************************************************* + /// Erases an element. + ///\param key The key to erase. + ///\return The number of elements erased. + //********************************************************************* + size_t erase(key_value_parameter_t key) + { + size_t count = 0; + size_t bucket_id = bucket(key); + + bucket_t& bucket = (*pbuckets)[bucket_id]; + + local_iterator iprevious = bucket.before_begin(); + local_iterator icurrent = bucket.begin(); + + while (icurrent != bucket.end()) + { + if (icurrent->key_value_pair.first == key) + { + bucket.erase_after(iprevious); + ++count; + icurrent = iprevious; + } + else + { + ++iprevious; + } + + ++icurrent; + } + + return count; + } + + //********************************************************************* + /// Erases an element. + ///\param ielement Iterator to the element. + //********************************************************************* + iterator erase(const_iterator ielement) + { + // Make a note of the next one. + iterator inext(pbuckets->end(), ielement.get_bucket_list_iterator(), ielement.get_local_iterator()); + ++inext; + + bucket_t& bucket = ielement.get_bucket(); + local_iterator icurrent = ielement.get_local_iterator(); + local_iterator iprevious = bucket.before_begin(); + + // Find the node we're interested in. + while (iprevious->etl_next != &*icurrent) + { + ++iprevious; + } + + bucket.erase_after(iprevious); + + return inext; + } + + //********************************************************************* + /// Erases a range of elements. + /// The range includes all the elements between first and last, including the + /// element pointed by first, but not the one pointed to by last. + ///\param first Iterator to the first element. + ///\param last Iterator to the last element. + //********************************************************************* + iterator erase(const_iterator first, const_iterator last) + { + // Make a note of the last. + iterator result(pbuckets->end(), last.get_bucket_list_iterator(), last.get_local_iterator()); + + // Get the starting point. + bucket_list_iterator ibucket = first.get_bucket_list_iterator(); + local_iterator ifirst = first.get_local_iterator(); + local_iterator iprevious = ibucket->before_begin(); + local_iterator iend; + + // Find the first node we're interested in. + while (iprevious->etl_next != &*ifirst) + { + ++iprevious; + } + + iend = iprevious; + iend++; + + while (first != last) + { + // Find how far we can go in this bucket. + while ((first != last) && (iend != ibucket->end())) + { + ++first; + ++iend; + } + + // Erase the range. + ibucket->erase_after(iprevious, iend); + + // At the end of this bucket? + if (iend == ibucket->end()) + { + // Move on to the next bucket. + ++ibucket; + iprevious = ibucket->before_begin(); + iend = iprevious; + ++iend; + } + else + { + // Still in the same bucket. + iprevious = iend; + } + } + + return result; + } + + //************************************************************************* + /// Clears the unordered_multimap. + //************************************************************************* + void clear() + { + initialise(); + } + + //********************************************************************* + /// Counts an element. + ///\param key The key to search for. + ///\return 1 if the key exists, otherwise 0. + //********************************************************************* + size_t count(key_value_parameter_t key) const + { + size_t n = 0; + const_iterator first = find(key); + const_iterator last = first; + + if (last != end()) + { + ++last; + ++n; + + while ((last != end()) && (key == last->first)) + { + ++last; + ++n; + } + } + + return n; + } + + //********************************************************************* + /// Finds an element. + ///\param key The key to search for. + ///\return An iterator to the element if the key exists, otherwise end(). + //********************************************************************* + iterator find(key_value_parameter_t key) + { + size_t index = bucket(key); + + bucket_list_iterator ibucket = pbuckets->begin() + index; + bucket_t& bucket = *ibucket; + + // Is the bucket not empty? + if (!bucket.empty()) + { + // Step though the list until we find the end or an equivalent key. + local_iterator inode = bucket.begin(); + local_iterator iend = bucket.end(); + + while (inode != iend) + { + // Do we have this one? + if (key_equal_function(key, inode->key_value_pair.first)) + { + return iterator(pbuckets->end(), ibucket, inode); + } + + ++inode; + } + } + + return end(); + } + + //********************************************************************* + /// Finds an element. + ///\param key The key to search for. + ///\return An iterator to the element if the key exists, otherwise end(). + //********************************************************************* + const_iterator find(key_value_parameter_t key) const + { + size_t index = bucket(key); + + bucket_list_iterator ibucket = pbuckets->begin() + index; + bucket_t& bucket = *ibucket; + + // Is the bucket not empty? + if (!bucket.empty()) + { + // Step though the list until we find the end or an equivalent key. + local_iterator inode = bucket.begin(); + local_iterator iend = bucket.end(); + + while (inode != iend) + { + // Do we have this one? + if (key_equal_function(key, inode->key_value_pair.first)) + { + return const_iterator(pbuckets->end(), ibucket, inode); + } + + ++inode; + } + } + + return end(); + } + + //********************************************************************* + /// Returns a range containing all elements with key key in the container. + /// The range is defined by two iterators, the first pointing to the first + /// element of the wanted range and the second pointing past the last + /// element of the range. + ///\param key The key to search for. + ///\return An iterator pair to the range of elements if the key exists, otherwise end(). + //********************************************************************* + std::pair equal_range(const key_value_parameter_t& key) + { + iterator first = find(key); + iterator last = first; + + if (last != end()) + { + ++last; + + while ((last != end()) && (key == last->first)) + { + ++last; + } + } + + return std::pair(first, last); + } + + //********************************************************************* + /// Returns a range containing all elements with key key in the container. + /// The range is defined by two iterators, the first pointing to the first + /// element of the wanted range and the second pointing past the last + /// element of the range. + ///\param key The key to search for. + ///\return A const iterator pair to the range of elements if the key exists, otherwise end(). + //********************************************************************* + std::pair equal_range(const key_value_parameter_t& key) const + { + const_iterator first = find(key); + const_iterator last = first; + + if (last != end()) + { + ++last; + + while ((last != end()) && (key == last->first)) + { + ++last; + } + } + + return std::pair(first, last); + } + + //************************************************************************* + /// Gets the size of the unordered_multimap. + //************************************************************************* + size_type size() const + { + return pnodepool->size(); + } + + //************************************************************************* + /// Gets the maximum possible size of the unordered_multimap. + //************************************************************************* + size_type max_size() const + { + return pnodepool->max_size(); + } + + //************************************************************************* + /// Checks to see if the unordered_multimap is empty. + //************************************************************************* + bool empty() const + { + return pnodepool->empty(); + } + + //************************************************************************* + /// Checks to see if the unordered_multimap is full. + //************************************************************************* + bool full() const + { + return pnodepool->full(); + } + + //************************************************************************* + /// Returns the remaining capacity. + ///\return The remaining capacity. + //************************************************************************* + size_t available() const + { + return pnodepool->available(); + } + + //************************************************************************* + /// Returns the load factor = size / bucket_count. + ///\return The load factor = size / bucket_count. + //************************************************************************* + float load_factor() const + { + return static_cast(size()) / static_cast(bucket_count()); + } + + //************************************************************************* + /// Returns the function that hashes the keys. + ///\return The function that hashes the keys.. + //************************************************************************* + hasher hash_function() const + { + return key_hash_function; + } + + //************************************************************************* + /// Returns the function that compares the keys. + ///\return The function that compares the keys.. + //************************************************************************* + key_equal key_eq() const + { + return key_equal_function; + } + + //************************************************************************* + /// Assignment operator. + //************************************************************************* + iunordered_multimap& operator = (const iunordered_multimap& rhs) + { + // Skip if doing self assignment + if (this != &rhs) + { + assign(rhs.cbegin(), rhs.cend()); + } + + return *this; + } + + protected: + + //********************************************************************* + /// Constructor. + //********************************************************************* + iunordered_multimap(pool_t& node_pool, bucket_list_t& buckets) + : pnodepool(&node_pool), + pbuckets(&buckets) + { + } + + //********************************************************************* + /// Initialise the unordered_multimap. + //********************************************************************* + void initialise() + { + pbuckets->resize(pnodepool->max_size()); + + if (!empty()) + { + pnodepool->release_all(); + + for (size_t i = 0; i < pbuckets->size(); ++i) + { + (*pbuckets)[i].clear(); + } + } + + first = pbuckets->begin(); + last = first; + } + + private: + + //********************************************************************* + /// Adjust the first and last markers according to the new entry. + //********************************************************************* + void adjust_first_last_markers(bucket_list_iterator ibucket) + { + if (ibucket < first) + { + first = ibucket; + } + else if (ibucket > last) + { + last = ibucket; + } + } + + // Disable copy construction. + iunordered_multimap(const iunordered_multimap&); + + /// The pool of data nodes used in the list. + pool_t* pnodepool; + + /// The bucket list. + bucket_list_t* pbuckets; + + /// The first and last iterators to buckets with values. + bucket_list_iterator first; + bucket_list_iterator last; + + /// The function that creates the hashes. + hasher key_hash_function; + + /// The function that compares the keys for equality. + key_equal key_equal_function; + }; + + //*************************************************************************** + /// Equal operator. + ///\param lhs Reference to the first unordered_multimap. + ///\param rhs Reference to the second unordered_multimap. + ///\return true if the arrays are equal, otherwise false + ///\ingroup unordered_multimap + //*************************************************************************** + template + bool operator ==(const etl::iunordered_multimap& lhs, const etl::iunordered_multimap& rhs) + { + return (lhs.size() == rhs.size()) && std::equal(lhs.begin(), lhs.end(), rhs.begin()); + } + + //*************************************************************************** + /// Not equal operator. + ///\param lhs Reference to the first unordered_multimap. + ///\param rhs Reference to the second unordered_multimap. + ///\return true if the arrays are not equal, otherwise false + ///\ingroup unordered_multimap + //*************************************************************************** + template + bool operator !=(const etl::iunordered_multimap& lhs, const etl::iunordered_multimap& rhs) + { + return !(lhs == rhs); + } +} + +#undef ETL_FILE +#endif diff --git a/iunordered_multiset.h b/iunordered_multiset.h new file mode 100644 index 00000000..a5f1cf5d --- /dev/null +++ b/iunordered_multiset.h @@ -0,0 +1,1183 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2016 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_IUNORDERED_MULTISET__ +#define __ETL_IUNORDERED_MULTISET__ + +#include +#include +#include +#include +#include + +#include "type_traits.h" +#include "parameter_type.h" +#include "hash.h" +#include "nullptr.h" +#include "ipool.h" +#include "ivector.h" +#include "error_handler.h" +#include "intrusive_forward_list.h" +#include "exception.h" +#include "error_handler.h" + +#undef ETL_FILE +#define ETL_FILE "23" + +namespace etl +{ + //*************************************************************************** + /// Exception for the unordered_multiset. + ///\ingroup unordered_multiset + //*************************************************************************** + class unordered_multiset_exception : public exception + { + public: + + unordered_multiset_exception(string_type what, string_type file_name, numeric_type line_number) + : exception(what, file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Full exception for the unordered_multiset. + ///\ingroup unordered_multiset + //*************************************************************************** + class unordered_multiset_full : public unordered_multiset_exception + { + public: + + unordered_multiset_full(string_type file_name, numeric_type line_number) + : unordered_multiset_exception(ETL_ERROR_TEXT("unordered_multiset:full", ETL_FILE"A"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Out of range exception for the unordered_multiset. + ///\ingroup unordered_multiset + //*************************************************************************** + class unordered_multiset_out_of_range : public unordered_multiset_exception + { + public: + + unordered_multiset_out_of_range(string_type file_name, numeric_type line_number) + : unordered_multiset_exception(ETL_ERROR_TEXT("unordered_multiset:range", ETL_FILE"B"), file_name, line_number) + {} + }; + + //*************************************************************************** + /// Iterator exception for the unordered_multiset. + ///\ingroup unordered_multiset + //*************************************************************************** + class unordered_multiset_iterator : public unordered_multiset_exception + { + public: + + unordered_multiset_iterator(string_type file_name, numeric_type line_number) + : unordered_multiset_exception(ETL_ERROR_TEXT("unordered_multiset:iterator", ETL_FILE"C"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// The base class for specifically sized unordered_multiset. + /// Can be used as a reference type for all unordered_multiset containing a specific type. + ///\ingroup unordered_multiset + //*************************************************************************** + template , typename TKeyEqual = std::equal_to > + class iunordered_multiset + { + public: + + typedef TKey value_type; + typedef TKey key_type; + typedef THash hasher; + typedef TKeyEqual key_equal; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef size_t size_type; + + typedef typename parameter_type::type key_value_parameter_t; + + typedef etl::forward_link<> link_t; + + // The nodes that store the elements. + struct node_t : public link_t + { + node_t(const value_type& key) + : key(key) + { + } + + value_type key; + }; + + private: + + typedef etl::intrusive_forward_list bucket_t; + typedef etl::ipool pool_t; + typedef etl::ivector bucket_list_t; + + typedef typename bucket_list_t::iterator bucket_list_iterator; + + public: + + // Local iterators iterate over one bucket. + typedef typename bucket_t::iterator local_iterator; + typedef typename bucket_t::const_iterator local_const_iterator; + + //********************************************************************* + class iterator : public std::iterator + { + public: + + typedef typename iunordered_multiset::value_type value_type; + typedef typename iunordered_multiset::key_type key_type; + typedef typename iunordered_multiset::hasher hasher; + typedef typename iunordered_multiset::key_equal key_equal; + typedef typename iunordered_multiset::reference reference; + typedef typename iunordered_multiset::const_reference const_reference; + typedef typename iunordered_multiset::pointer pointer; + typedef typename iunordered_multiset::const_pointer const_pointer; + typedef typename iunordered_multiset::size_type size_type; + + friend class iunordered_multiset; + + //********************************* + iterator() + { + } + + //********************************* + iterator(const iterator& other) + : ibuckets_end(other.ibuckets_end), + ibucket(other.ibucket), + inode(other.inode) + { + } + + //********************************* + iterator& operator ++() + { + ++inode; + + // The end of this node list? + if (inode == ibucket->end()) + { + // Search for the next non-empty bucket. + ++ibucket; + while ((ibucket != ibuckets_end) && (ibucket->empty())) + { + ++ibucket; + } + + // If not past the end, get the first node in the bucket. + if (ibucket != ibuckets_end) + { + inode = ibucket->begin(); + } + } + + return *this; + } + + //********************************* + iterator operator ++(int) + { + iterator temp(*this); + operator++(); + return temp; + } + + //********************************* + iterator operator =(const iterator& other) + { + ibuckets_end = other.ibuckets_end; + ibucket = other.ibucket; + inode = other.inode; + return *this; + } + + //********************************* + reference operator *() + { + return inode->key; + } + + //********************************* + const_reference operator *() const + { + return inode->key; + } + + //********************************* + pointer operator &() + { + return &(inode->key); + } + + //********************************* + const_pointer operator &() const + { + return &(inode->key); + } + + //********************************* + pointer operator ->() + { + return &(inode->key); + } + + //********************************* + const_pointer operator ->() const + { + return &(inode->key); + } + + //********************************* + friend bool operator == (const iterator& lhs, const iterator& rhs) + { + return lhs.compare(rhs); + } + + //********************************* + friend bool operator != (const iterator& lhs, const iterator& rhs) + { + return !(lhs == rhs); + } + + private: + + //********************************* + iterator(bucket_list_iterator ibuckets_end, bucket_list_iterator ibucket, local_iterator inode) + : ibuckets_end(ibuckets_end), + ibucket(ibucket), + inode(inode) + { + } + + //********************************* + bool compare(const iterator& rhs) const + { + return rhs.inode == inode; + } + + //********************************* + bucket_t& get_bucket() + { + return *ibucket; + } + + //********************************* + bucket_list_iterator& get_bucket_list_iterator() + { + return ibucket; + } + + //********************************* + local_iterator get_local_iterator() + { + return inode; + } + + bucket_list_iterator ibuckets_end; + bucket_list_iterator ibucket; + local_iterator inode; + }; + + //********************************************************************* + class const_iterator : public std::iterator + { + public: + + typedef typename iunordered_multiset::value_type value_type; + typedef typename iunordered_multiset::key_type key_type; + typedef typename iunordered_multiset::hasher hasher; + typedef typename iunordered_multiset::key_equal key_equal; + typedef typename iunordered_multiset::reference reference; + typedef typename iunordered_multiset::const_reference const_reference; + typedef typename iunordered_multiset::pointer pointer; + typedef typename iunordered_multiset::const_pointer const_pointer; + typedef typename iunordered_multiset::size_type size_type; + + friend class iunordered_multiset; + friend class iterator; + + //********************************* + const_iterator() + { + } + + //********************************* + const_iterator(const typename iunordered_multiset::iterator& other) + : ibuckets_end(other.ibuckets_end), + ibucket(other.ibucket), + inode(other.inode) + { + } + + //********************************* + const_iterator(const const_iterator& other) + : ibuckets_end(other.ibuckets_end), + ibucket(other.ibucket), + inode(other.inode) + { + } + + //********************************* + const_iterator& operator ++() + { + ++inode; + + // The end of this node list? + if (inode == ibucket->end()) + { + // Search for the next non-empty bucket. + + ++ibucket; + while ((ibucket != ibuckets_end) && (ibucket->empty())) + { + ++ibucket; + } + + // If not past the end, get the first node in the bucket. + if (ibucket != ibuckets_end) + { + inode = ibucket->begin(); + } + } + + return *this; + } + + //********************************* + const_iterator operator ++(int) + { + const_iterator temp(*this); + operator++(); + return temp; + } + + //********************************* + const_iterator operator =(const const_iterator& other) + { + ibuckets_end = other.ibuckets_end; + ibucket = other.ibucket; + inode = other.inode; + return *this; + } + + //********************************* + const_reference operator *() const + { + return inode->key; + } + + //********************************* + const_pointer operator &() const + { + return &(inode->key); + } + + //********************************* + const_pointer operator ->() const + { + return &(inode->key); + } + + //********************************* + friend bool operator == (const const_iterator& lhs, const const_iterator& rhs) + { + return lhs.compare(rhs); + } + + //********************************* + friend bool operator != (const const_iterator& lhs, const const_iterator& rhs) + { + return !(lhs == rhs); + } + + private: + + //********************************* + const_iterator(bucket_list_iterator ibuckets_end, bucket_list_iterator ibucket, local_iterator inode) + : ibuckets_end(ibuckets_end), + ibucket(ibucket), + inode(inode) + { + } + + //********************************* + bool compare(const const_iterator& rhs) const + { + return rhs.inode == inode; + } + + //********************************* + bucket_t& get_bucket() + { + return *ibucket; + } + + //********************************* + bucket_list_iterator& get_bucket_list_iterator() + { + return ibucket; + } + + //********************************* + local_iterator get_local_iterator() + { + return inode; + } + + bucket_list_iterator ibuckets_end; + bucket_list_iterator ibucket; + local_iterator inode; + }; + + typedef typename std::iterator_traits::difference_type difference_type; + + //********************************************************************* + /// Returns an iterator to the beginning of the unordered_multiset. + ///\return An iterator to the beginning of the unordered_multiset. + //********************************************************************* + iterator begin() + { + return iterator(pbuckets->end(), first, first->begin()); + } + + //********************************************************************* + /// Returns a const_iterator to the beginning of the unordered_multiset. + ///\return A const iterator to the beginning of the unordered_multiset. + //********************************************************************* + const_iterator begin() const + { + return const_iterator(pbuckets->end(), first, first->begin()); + } + + //********************************************************************* + /// Returns a const_iterator to the beginning of the unordered_multiset. + ///\return A const iterator to the beginning of the unordered_multiset. + //********************************************************************* + const_iterator cbegin() const + { + return const_iterator(pbuckets->end(), first, first->begin()); + } + + //********************************************************************* + /// Returns an iterator to the beginning of the unordered_multiset bucket. + ///\return An iterator to the beginning of the unordered_multiset bucket. + //********************************************************************* + local_iterator begin(size_t i) + { + return (*pbuckets)[i].begin(); + } + + //********************************************************************* + /// Returns a const_iterator to the beginning of the unordered_multiset bucket. + ///\return A const iterator to the beginning of the unordered_multiset bucket. + //********************************************************************* + local_const_iterator begin(size_t i) const + { + return (*pbuckets)[i].cbegin(); + } + + //********************************************************************* + /// Returns a const_iterator to the beginning of the unordered_multiset bucket. + ///\return A const iterator to the beginning of the unordered_multiset bucket. + //********************************************************************* + local_const_iterator cbegin(size_t i) const + { + return (*pbuckets)[i].cbegin(); + } + + //********************************************************************* + /// Returns an iterator to the end of the unordered_multiset. + ///\return An iterator to the end of the unordered_multiset. + //********************************************************************* + iterator end() + { + return iterator(pbuckets->end(), last, last->end()); + } + + //********************************************************************* + /// Returns a const_iterator to the end of the unordered_multiset. + ///\return A const iterator to the end of the unordered_multiset. + //********************************************************************* + const_iterator end() const + { + return const_iterator(pbuckets->end(), last, last->end()); + } + + //********************************************************************* + /// Returns a const_iterator to the end of the unordered_multiset. + ///\return A const iterator to the end of the unordered_multiset. + //********************************************************************* + const_iterator cend() const + { + return const_iterator(pbuckets->end(), last, last->end()); + } + + //********************************************************************* + /// Returns an iterator to the end of the unordered_multiset bucket. + ///\return An iterator to the end of the unordered_multiset bucket. + //********************************************************************* + local_iterator end(size_t i) + { + return (*pbuckets)[i].end(); + } + + //********************************************************************* + /// Returns a const_iterator to the end of the unordered_multiset bucket. + ///\return A const iterator to the end of the unordered_multiset bucket. + //********************************************************************* + local_const_iterator end(size_t i) const + { + return (*pbuckets)[i].cend(); + } + + //********************************************************************* + /// Returns a const_iterator to the end of the unordered_multiset bucket. + ///\return A const iterator to the end of the unordered_multiset bucket. + //********************************************************************* + local_const_iterator cend(size_t i) const + { + return (*pbuckets)[i].cend(); + } + + //********************************************************************* + /// Returns the bucket index for the key. + ///\return The bucket index for the key. + //********************************************************************* + size_type bucket(key_value_parameter_t key) const + { + return key_hash_function(key) % pbuckets->size(); + } + + //********************************************************************* + /// Returns the size of the bucket key. + ///\return The bucket size of the bucket key. + //********************************************************************* + size_type bucket_size(key_value_parameter_t key) const + { + size_t index = bucket(key); + + return std::distance((*pbuckets)[index].begin(), (*pbuckets)[index].end()); + } + + //********************************************************************* + /// Returns the maximum number of the buckets the container can hold. + ///\return The maximum number of the buckets the container can hold. + //********************************************************************* + size_type max_bucket_count() const + { + return max_size(); + } + + //********************************************************************* + /// Returns the number of the buckets the container holds. + ///\return The number of the buckets the container holds. + //********************************************************************* + size_type bucket_count() const + { + return max_size(); + } + + //********************************************************************* + /// Assigns values to the unordered_multiset. + /// If asserts or exceptions are enabled, emits unordered_multiset_full if the unordered_multiset does not have enough free space. + /// If asserts or exceptions are enabled, emits unordered_multiset_iterator if the iterators are reversed. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + //********************************************************************* + template + void assign(TIterator first, TIterator last) + { +#ifdef _DEBUG + difference_type count = std::distance(first, last); + ETL_ASSERT(count >= 0, ETL_ERROR(unordered_multiset_iterator)); + ETL_ASSERT(size_t(count) <= max_size() , ETL_ERROR(unordered_multiset_full)); +#endif + + clear(); + + while (first != last) + { + insert(*first++); + } + } + + //********************************************************************* + /// Inserts a value to the unordered_multiset. + /// If asserts or exceptions are enabled, emits unordered_multiset_full if the unordered_multiset is already full. + ///\param value The value to insert. + //********************************************************************* + std::pair insert(const value_type& key) + { + std::pair result(end(), false); + + ETL_ASSERT(!full(), ETL_ERROR(unordered_multiset_full)); + + // Get the hash index. + size_t index = bucket(key); + + // Get the bucket & bucket iterator. + bucket_list_iterator ibucket = pbuckets->begin() + index; + bucket_t& bucket = *ibucket; + + // The first one in the bucket? + if (bucket.empty()) + { + // Get a new node. + node_t& node = *pnodepool->allocate(node_t(key)); + + // Just add the pointer to the bucket; + bucket.insert_after(bucket.before_begin(), node); + + result.first = iterator(pbuckets->end(), ibucket, ibucket->begin()); + result.second = true; + + adjust_first_last_markers(ibucket); + } + else + { + // Step though the bucket looking for a place to insert. + local_iterator inode_previous = bucket.before_begin(); + local_iterator inode = bucket.begin(); + + while (inode != bucket.end()) + { + // Do we already have this key? + if (inode->key == key) + { + break; + } + + ++inode_previous; + ++inode; + } + + // Get a new node. + node_t& node = *pnodepool->allocate(node_t(key)); + + // Add the node to the end of the bucket; + bucket.insert_after(inode_previous, node); + ++inode_previous; + + result.first = iterator(pbuckets->end(), ibucket, inode_previous); + result.second = true; + } + + return result; + } + + //********************************************************************* + /// Inserts a value to the unordered_multiset. + /// If asserts or exceptions are enabled, emits unordered_multiset_full if the unordered_multiset is already full. + ///\param position The position to insert at. + ///\param value The value to insert. + //********************************************************************* + iterator insert(const_iterator position, const value_type& key) + { + return insert(key).first; + } + + //********************************************************************* + /// Inserts a range of values to the unordered_multiset. + /// If asserts or exceptions are enabled, emits unordered_multiset_full if the unordered_multiset does not have enough free space. + ///\param position The position to insert at. + ///\param first The first element to add. + ///\param last The last + 1 element to add. + //********************************************************************* + template + void insert(TIterator first, TIterator last) + { + while (first != last) + { + insert(*first++); + } + } + + //********************************************************************* + /// Erases an element. + ///\param key The key to erase. + ///\return The number of elements erased. + //********************************************************************* + size_t erase(key_value_parameter_t key) + { + size_t count = 0; + size_t bucket_id = bucket(key); + + bucket_t& bucket = (*pbuckets)[bucket_id]; + + local_iterator iprevious = bucket.before_begin(); + local_iterator icurrent = bucket.begin(); + + while (icurrent != bucket.end()) + { + if (icurrent->key == key) + { + bucket.erase_after(iprevious); + ++count; + icurrent = iprevious; + } + else + { + ++iprevious; + } + + ++icurrent; + } + + return count; + } + + //********************************************************************* + /// Erases an element. + ///\param ielement Iterator to the element. + //********************************************************************* + iterator erase(const_iterator ielement) + { + // Make a note of the next one. + iterator inext(pbuckets->end(), ielement.get_bucket_list_iterator(), ielement.get_local_iterator()); + ++inext; + + bucket_t& bucket = ielement.get_bucket(); + local_iterator icurrent = ielement.get_local_iterator(); + local_iterator iprevious = bucket.before_begin(); + + // Find the node we're interested in. + while (iprevious->etl_next != &*icurrent) + { + ++iprevious; + } + + bucket.erase_after(iprevious); + + return inext; + } + + //********************************************************************* + /// Erases a range of elements. + /// The range includes all the elements between first and last, including the + /// element pointed by first, but not the one pointed to by last. + ///\param first Iterator to the first element. + ///\param last Iterator to the last element. + //********************************************************************* + iterator erase(const_iterator first, const_iterator last) + { + // Make a note of the last. + iterator result(pbuckets->end(), last.get_bucket_list_iterator(), last.get_local_iterator()); + + // Get the starting point. + bucket_list_iterator ibucket = first.get_bucket_list_iterator(); + local_iterator ifirst = first.get_local_iterator(); + local_iterator iprevious = ibucket->before_begin(); + local_iterator iend; + + // Find the first node we're interested in. + while (iprevious->etl_next != &*ifirst) + { + ++iprevious; + } + + iend = iprevious; + iend++; + + while (first != last) + { + // Find how far we can go in this bucket. + while ((first != last) && (iend != ibucket->end())) + { + ++first; + ++iend; + } + + // Erase the range. + ibucket->erase_after(iprevious, iend); + + // At the end of this bucket? + if (iend == ibucket->end()) + { + // Move on to the next bucket. + ++ibucket; + iprevious = ibucket->before_begin(); + iend = iprevious; + ++iend; + } + else + { + // Still in the same bucket. + iprevious = iend; + } + } + + return result; + } + + //************************************************************************* + /// Clears the unordered_multiset. + //************************************************************************* + void clear() + { + initialise(); + } + + //********************************************************************* + /// Counts an element. + ///\param key The key to search for. + ///\return 1 if the key exists, otherwise 0. + //********************************************************************* + size_t count(key_value_parameter_t key) const + { + size_t n = 0; + const_iterator first = find(key); + const_iterator last = first; + + if (last != end()) + { + ++last; + ++n; + + while ((last != end()) && (key == *last)) + { + ++last; + ++n; + } + } + + return n; + } + + //********************************************************************* + /// Finds an element. + ///\param key The key to search for. + ///\return An iterator to the element if the key exists, otherwise end(). + //********************************************************************* + iterator find(key_value_parameter_t key) + { + size_t index = bucket(key); + + bucket_list_iterator ibucket = pbuckets->begin() + index; + bucket_t& bucket = *ibucket; + + // Is the bucket not empty? + if (!bucket.empty()) + { + // Step though the list until we find the end or an equivalent key. + local_iterator inode = bucket.begin(); + local_iterator iend = bucket.end(); + + while (inode != iend) + { + // Do we have this one? + if (key_equal_function(key, inode->key)) + { + return iterator(pbuckets->end(), ibucket, inode); + } + + ++inode; + } + } + + return end(); + } + + //********************************************************************* + /// Finds an element. + ///\param key The key to search for. + ///\return An iterator to the element if the key exists, otherwise end(). + //********************************************************************* + const_iterator find(key_value_parameter_t key) const + { + size_t index = bucket(key); + + bucket_list_iterator ibucket = pbuckets->begin() + index; + bucket_t& bucket = *ibucket; + + // Is the bucket not empty? + if (!bucket.empty()) + { + // Step though the list until we find the end or an equivalent key. + local_iterator inode = bucket.begin(); + local_iterator iend = bucket.end(); + + while (inode != iend) + { + // Do we have this one? + if (key_equal_function(key, inode->key)) + { + return iterator(pbuckets->end(), ibucket, inode); + } + + ++inode; + } + } + + return end(); + } + + //********************************************************************* + /// Returns a range containing all elements with key key in the container. + /// The range is defined by two iterators, the first pointing to the first + /// element of the wanted range and the second pointing past the last + /// element of the range. + ///\param key The key to search for. + ///\return An iterator pair to the range of elements if the key exists, otherwise end(). + //********************************************************************* + std::pair equal_range(const key_value_parameter_t& key) + { + iterator first = find(key); + iterator last = first; + + if (last != end()) + { + ++last; + + while ((last != end()) && (key == *last)) + { + ++last; + } + } + + return std::pair(first, last); + } + + //********************************************************************* + /// Returns a range containing all elements with key key in the container. + /// The range is defined by two iterators, the first pointing to the first + /// element of the wanted range and the second pointing past the last + /// element of the range. + ///\param key The key to search for. + ///\return A const iterator pair to the range of elements if the key exists, otherwise end(). + //********************************************************************* + std::pair equal_range(const key_value_parameter_t& key) const + { + const_iterator first = find(key); + const_iterator last = first; + + if (last != end()) + { + ++last; + + while ((last != end()) && (key == *last)) + { + ++last; + } + } + + return std::pair(first, last); + } + + //************************************************************************* + /// Gets the size of the unordered_multiset. + //************************************************************************* + size_type size() const + { + return pnodepool->size(); + } + + //************************************************************************* + /// Gets the maximum possible size of the unordered_multiset. + //************************************************************************* + size_type max_size() const + { + return pnodepool->max_size(); + } + + //************************************************************************* + /// Checks to see if the unordered_multiset is empty. + //************************************************************************* + bool empty() const + { + return pnodepool->empty(); + } + + //************************************************************************* + /// Checks to see if the unordered_multiset is full. + //************************************************************************* + bool full() const + { + return pnodepool->full(); + } + + //************************************************************************* + /// Returns the remaining capacity. + ///\return The remaining capacity. + //************************************************************************* + size_t available() const + { + return pnodepool->available(); + } + + //************************************************************************* + /// Returns the load factor = size / bucket_count. + ///\return The load factor = size / bucket_count. + //************************************************************************* + float load_factor() const + { + return static_cast(size()) / static_cast(bucket_count()); + } + + //************************************************************************* + /// Returns the function that hashes the keys. + ///\return The function that hashes the keys.. + //************************************************************************* + hasher hash_function() const + { + return key_hash_function; + } + + //************************************************************************* + /// Returns the function that compares the keys. + ///\return The function that compares the keys.. + //************************************************************************* + key_equal key_eq() const + { + return key_equal_function; + } + + //************************************************************************* + /// Assignment operator. + //************************************************************************* + iunordered_multiset& operator = (const iunordered_multiset& rhs) + { + // Skip if doing self assignment + if (this != &rhs) + { + assign(rhs.cbegin(), rhs.cend()); + } + + return *this; + } + + protected: + + //********************************************************************* + /// Constructor. + //********************************************************************* + iunordered_multiset(pool_t& node_pool, bucket_list_t& buckets) + : pnodepool(&node_pool), + pbuckets(&buckets) + { + } + + //********************************************************************* + /// Initialise the unordered_multiset. + //********************************************************************* + void initialise() + { + pbuckets->resize(pnodepool->max_size()); + + if (!empty()) + { + pnodepool->release_all(); + + for (size_t i = 0; i < pbuckets->size(); ++i) + { + (*pbuckets)[i].clear(); + } + } + + first = pbuckets->begin(); + last = first; + } + + private: + + //********************************************************************* + /// Adjust the first and last markers according to the new entry. + //********************************************************************* + void adjust_first_last_markers(bucket_list_iterator ibucket) + { + if (ibucket < first) + { + first = ibucket; + } + else if (ibucket > last) + { + last = ibucket; + } + } + + // Disable copy construction. + iunordered_multiset(const iunordered_multiset&); + + /// The pool of data nodes used in the list. + pool_t* pnodepool; + + /// The bucket list. + bucket_list_t* pbuckets; + + /// The first and last iterators to buckets with values. + bucket_list_iterator first; + bucket_list_iterator last; + + /// The function that creates the hashes. + hasher key_hash_function; + + /// The function that compares the keys for equality. + key_equal key_equal_function; + }; + + //*************************************************************************** + /// Equal operator. + ///\param lhs Reference to the first unordered_multiset. + ///\param rhs Reference to the second unordered_multiset. + ///\return true if the arrays are equal, otherwise false + ///\ingroup unordered_multiset + //*************************************************************************** + template + bool operator ==(const etl::iunordered_multiset& lhs, const etl::iunordered_multiset& rhs) + { + return (lhs.size() == rhs.size()) && std::equal(lhs.begin(), lhs.end(), rhs.begin()); + } + + //*************************************************************************** + /// Not equal operator. + ///\param lhs Reference to the first unordered_multiset. + ///\param rhs Reference to the second unordered_multiset. + ///\return true if the arrays are not equal, otherwise false + ///\ingroup unordered_multiset + //*************************************************************************** + template + bool operator !=(const etl::iunordered_multiset& lhs, const etl::iunordered_multiset& rhs) + { + return !(lhs == rhs); + } +} + +#undef ETL_FILE +#endif diff --git a/iunordered_set.h b/iunordered_set.h new file mode 100644 index 00000000..3debe2ad --- /dev/null +++ b/iunordered_set.h @@ -0,0 +1,1157 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2016 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_IUNORDERED_SET__ +#define __ETL_IUNORDERED_SET__ + +#include +#include +#include +#include +#include + +#include "type_traits.h" +#include "parameter_type.h" +#include "hash.h" +#include "nullptr.h" +#include "ipool.h" +#include "ivector.h" +#include "error_handler.h" +#include "intrusive_forward_list.h" +#include "exception.h" +#include "error_handler.h" + +#undef ETL_FILE +#define ETL_FILE "23" + +namespace etl +{ + //*************************************************************************** + /// Exception for the unordered_set. + ///\ingroup unordered_set + //*************************************************************************** + class unordered_set_exception : public exception + { + public: + + unordered_set_exception(string_type what, string_type file_name, numeric_type line_number) + : exception(what, file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Full exception for the unordered_set. + ///\ingroup unordered_set + //*************************************************************************** + class unordered_set_full : public unordered_set_exception + { + public: + + unordered_set_full(string_type file_name, numeric_type line_number) + : unordered_set_exception(ETL_ERROR_TEXT("unordered_set:full", ETL_FILE"A"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Out of range exception for the unordered_set. + ///\ingroup unordered_set + //*************************************************************************** + class unordered_set_out_of_range : public unordered_set_exception + { + public: + + unordered_set_out_of_range(string_type file_name, numeric_type line_number) + : unordered_set_exception(ETL_ERROR_TEXT("unordered_set:range", ETL_FILE"B"), file_name, line_number) + {} + }; + + //*************************************************************************** + /// Iterator exception for the unordered_set. + ///\ingroup unordered_set + //*************************************************************************** + class unordered_set_iterator : public unordered_set_exception + { + public: + + unordered_set_iterator(string_type file_name, numeric_type line_number) + : unordered_set_exception(ETL_ERROR_TEXT("unordered_set:iterator", ETL_FILE"C"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// The base class for specifically sized unordered_set. + /// Can be used as a reference type for all unordered_set containing a specific type. + ///\ingroup unordered_set + //*************************************************************************** + template , typename TKeyEqual = std::equal_to > + class iunordered_set + { + public: + + typedef TKey value_type; + typedef TKey key_type; + typedef THash hasher; + typedef TKeyEqual key_equal; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef size_t size_type; + + typedef typename parameter_type::type key_value_parameter_t; + + typedef etl::forward_link<> link_t; + + // The nodes that store the elements. + struct node_t : public link_t + { + node_t(const value_type& key) + : key(key) + { + } + + value_type key; + }; + + private: + + typedef etl::intrusive_forward_list bucket_t; + typedef etl::ipool pool_t; + typedef etl::ivector bucket_list_t; + + typedef typename bucket_list_t::iterator bucket_list_iterator; + + public: + + // Local iterators iterate over one bucket. + typedef typename bucket_t::iterator local_iterator; + typedef typename bucket_t::const_iterator local_const_iterator; + + //********************************************************************* + class iterator : public std::iterator + { + public: + + typedef typename iunordered_set::value_type value_type; + typedef typename iunordered_set::key_type key_type; + typedef typename iunordered_set::hasher hasher; + typedef typename iunordered_set::key_equal key_equal; + typedef typename iunordered_set::reference reference; + typedef typename iunordered_set::const_reference const_reference; + typedef typename iunordered_set::pointer pointer; + typedef typename iunordered_set::const_pointer const_pointer; + typedef typename iunordered_set::size_type size_type; + + friend class iunordered_set; + + //********************************* + iterator() + { + } + + //********************************* + iterator(const iterator& other) + : ibuckets_end(other.ibuckets_end), + ibucket(other.ibucket), + inode(other.inode) + { + } + + //********************************* + iterator& operator ++() + { + ++inode; + + // The end of this node list? + if (inode == ibucket->end()) + { + // Search for the next non-empty bucket. + ++ibucket; + while ((ibucket != ibuckets_end) && (ibucket->empty())) + { + ++ibucket; + } + + // If not past the end, get the first node in the bucket. + if (ibucket != ibuckets_end) + { + inode = ibucket->begin(); + } + } + + return *this; + } + + //********************************* + iterator operator ++(int) + { + iterator temp(*this); + operator++(); + return temp; + } + + //********************************* + iterator operator =(const iterator& other) + { + ibuckets_end = other.ibuckets_end; + ibucket = other.ibucket; + inode = other.inode; + return *this; + } + + //********************************* + reference operator *() + { + return inode->key; + } + + //********************************* + const_reference operator *() const + { + return inode->key; + } + + //********************************* + pointer operator &() + { + return &(inode->key); + } + + //********************************* + const_pointer operator &() const + { + return &(inode->key); + } + + //********************************* + pointer operator ->() + { + return &(inode->key); + } + + //********************************* + const_pointer operator ->() const + { + return &(inode->key); + } + + //********************************* + friend bool operator == (const iterator& lhs, const iterator& rhs) + { + return lhs.compare(rhs); + } + + //********************************* + friend bool operator != (const iterator& lhs, const iterator& rhs) + { + return !(lhs == rhs); + } + + private: + + //********************************* + iterator(bucket_list_iterator ibuckets_end, bucket_list_iterator ibucket, local_iterator inode) + : ibuckets_end(ibuckets_end), + ibucket(ibucket), + inode(inode) + { + } + + //********************************* + bool compare(const iterator& rhs) const + { + return rhs.inode == inode; + } + + //********************************* + bucket_t& get_bucket() + { + return *ibucket; + } + + //********************************* + bucket_list_iterator& get_bucket_list_iterator() + { + return ibucket; + } + + //********************************* + local_iterator get_local_iterator() + { + return inode; + } + + bucket_list_iterator ibuckets_end; + bucket_list_iterator ibucket; + local_iterator inode; + }; + + //********************************************************************* + class const_iterator : public std::iterator + { + public: + + typedef typename iunordered_set::value_type value_type; + typedef typename iunordered_set::key_type key_type; + typedef typename iunordered_set::hasher hasher; + typedef typename iunordered_set::key_equal key_equal; + typedef typename iunordered_set::reference reference; + typedef typename iunordered_set::const_reference const_reference; + typedef typename iunordered_set::pointer pointer; + typedef typename iunordered_set::const_pointer const_pointer; + typedef typename iunordered_set::size_type size_type; + + friend class iunordered_set; + friend class iterator; + + //********************************* + const_iterator() + { + } + + //********************************* + const_iterator(const typename iunordered_set::iterator& other) + : ibuckets_end(other.ibuckets_end), + ibucket(other.ibucket), + inode(other.inode) + { + } + + //********************************* + const_iterator(const const_iterator& other) + : ibuckets_end(other.ibuckets_end), + ibucket(other.ibucket), + inode(other.inode) + { + } + + //********************************* + const_iterator& operator ++() + { + ++inode; + + // The end of this node list? + if (inode == ibucket->end()) + { + // Search for the next non-empty bucket. + + ++ibucket; + while ((ibucket != ibuckets_end) && (ibucket->empty())) + { + ++ibucket; + } + + // If not past the end, get the first node in the bucket. + if (ibucket != ibuckets_end) + { + inode = ibucket->begin(); + } + } + + return *this; + } + + //********************************* + const_iterator operator ++(int) + { + const_iterator temp(*this); + operator++(); + return temp; + } + + //********************************* + const_iterator operator =(const const_iterator& other) + { + ibuckets_end = other.ibuckets_end; + ibucket = other.ibucket; + inode = other.inode; + return *this; + } + + //********************************* + const_reference operator *() const + { + return inode->key; + } + + //********************************* + const_pointer operator &() const + { + return &(inode->key); + } + + //********************************* + const_pointer operator ->() const + { + return &(inode->key); + } + + //********************************* + friend bool operator == (const const_iterator& lhs, const const_iterator& rhs) + { + return lhs.compare(rhs); + } + + //********************************* + friend bool operator != (const const_iterator& lhs, const const_iterator& rhs) + { + return !(lhs == rhs); + } + + private: + + //********************************* + const_iterator(bucket_list_iterator ibuckets_end, bucket_list_iterator ibucket, local_iterator inode) + : ibuckets_end(ibuckets_end), + ibucket(ibucket), + inode(inode) + { + } + + //********************************* + bool compare(const const_iterator& rhs) const + { + return rhs.inode == inode; + } + + //********************************* + bucket_t& get_bucket() + { + return *ibucket; + } + + //********************************* + bucket_list_iterator& get_bucket_list_iterator() + { + return ibucket; + } + + //********************************* + local_iterator get_local_iterator() + { + return inode; + } + + bucket_list_iterator ibuckets_end; + bucket_list_iterator ibucket; + local_iterator inode; + }; + + typedef typename std::iterator_traits::difference_type difference_type; + + //********************************************************************* + /// Returns an iterator to the beginning of the unordered_set. + ///\return An iterator to the beginning of the unordered_set. + //********************************************************************* + iterator begin() + { + return iterator(pbuckets->end(), first, first->begin()); + } + + //********************************************************************* + /// Returns a const_iterator to the beginning of the unordered_set. + ///\return A const iterator to the beginning of the unordered_set. + //********************************************************************* + const_iterator begin() const + { + return const_iterator(pbuckets->end(), first, first->begin()); + } + + //********************************************************************* + /// Returns a const_iterator to the beginning of the unordered_set. + ///\return A const iterator to the beginning of the unordered_set. + //********************************************************************* + const_iterator cbegin() const + { + return const_iterator(pbuckets->end(), first, first->begin()); + } + + //********************************************************************* + /// Returns an iterator to the beginning of the unordered_set bucket. + ///\return An iterator to the beginning of the unordered_set bucket. + //********************************************************************* + local_iterator begin(size_t i) + { + return (*pbuckets)[i].begin(); + } + + //********************************************************************* + /// Returns a const_iterator to the beginning of the unordered_set bucket. + ///\return A const iterator to the beginning of the unordered_set bucket. + //********************************************************************* + local_const_iterator begin(size_t i) const + { + return (*pbuckets)[i].cbegin(); + } + + //********************************************************************* + /// Returns a const_iterator to the beginning of the unordered_set bucket. + ///\return A const iterator to the beginning of the unordered_set bucket. + //********************************************************************* + local_const_iterator cbegin(size_t i) const + { + return (*pbuckets)[i].cbegin(); + } + + //********************************************************************* + /// Returns an iterator to the end of the unordered_set. + ///\return An iterator to the end of the unordered_set. + //********************************************************************* + iterator end() + { + return iterator(pbuckets->end(), last, last->end()); + } + + //********************************************************************* + /// Returns a const_iterator to the end of the unordered_set. + ///\return A const iterator to the end of the unordered_set. + //********************************************************************* + const_iterator end() const + { + return const_iterator(pbuckets->end(), last, last->end()); + } + + //********************************************************************* + /// Returns a const_iterator to the end of the unordered_set. + ///\return A const iterator to the end of the unordered_set. + //********************************************************************* + const_iterator cend() const + { + return const_iterator(pbuckets->end(), last, last->end()); + } + + //********************************************************************* + /// Returns an iterator to the end of the unordered_set bucket. + ///\return An iterator to the end of the unordered_set bucket. + //********************************************************************* + local_iterator end(size_t i) + { + return (*pbuckets)[i].end(); + } + + //********************************************************************* + /// Returns a const_iterator to the end of the unordered_set bucket. + ///\return A const iterator to the end of the unordered_set bucket. + //********************************************************************* + local_const_iterator end(size_t i) const + { + return (*pbuckets)[i].cend(); + } + + //********************************************************************* + /// Returns a const_iterator to the end of the unordered_set bucket. + ///\return A const iterator to the end of the unordered_set bucket. + //********************************************************************* + local_const_iterator cend(size_t i) const + { + return (*pbuckets)[i].cend(); + } + + //********************************************************************* + /// Returns the bucket index for the key. + ///\return The bucket index for the key. + //********************************************************************* + size_type bucket(key_value_parameter_t key) const + { + return key_hash_function(key) % pbuckets->size(); + } + + //********************************************************************* + /// Returns the size of the bucket key. + ///\return The bucket size of the bucket key. + //********************************************************************* + size_type bucket_size(key_value_parameter_t key) const + { + size_t index = bucket(key); + + return std::distance((*pbuckets)[index].begin(), (*pbuckets)[index].end()); + } + + //********************************************************************* + /// Returns the maximum number of the buckets the container can hold. + ///\return The maximum number of the buckets the container can hold. + //********************************************************************* + size_type max_bucket_count() const + { + return max_size(); + } + + //********************************************************************* + /// Returns the number of the buckets the container holds. + ///\return The number of the buckets the container holds. + //********************************************************************* + size_type bucket_count() const + { + return max_size(); + } + + //********************************************************************* + /// Assigns values to the unordered_set. + /// If asserts or exceptions are enabled, emits unordered_set_full if the unordered_set does not have enough free space. + /// If asserts or exceptions are enabled, emits unordered_set_iterator if the iterators are reversed. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + //********************************************************************* + template + void assign(TIterator first, TIterator last) + { +#ifdef _DEBUG + difference_type count = std::distance(first, last); + ETL_ASSERT(count >= 0, ETL_ERROR(unordered_set_iterator)); + ETL_ASSERT(size_t(count) <= max_size() , ETL_ERROR(unordered_set_full)); +#endif + + clear(); + + while (first != last) + { + insert(*first++); + } + } + + //********************************************************************* + /// Inserts a value to the unordered_set. + /// If asserts or exceptions are enabled, emits unordered_set_full if the unordered_set is already full. + ///\param value The value to insert. + //********************************************************************* + std::pair insert(const value_type& key) + { + std::pair result(end(), false); + + ETL_ASSERT(!full(), ETL_ERROR(unordered_set_full)); + + // Get the hash index. + size_t index = bucket(key); + + // Get the bucket & bucket iterator. + bucket_list_iterator ibucket = pbuckets->begin() + index; + bucket_t& bucket = *ibucket; + + // The first one in the bucket? + if (bucket.empty()) + { + // Get a new node. + node_t& node = *pnodepool->allocate(node_t(key)); + + // Just add the pointer to the bucket; + bucket.insert_after(bucket.before_begin(), node); + + result.first = iterator(pbuckets->end(), ibucket, ibucket->begin()); + result.second = true; + + adjust_first_last_markers(ibucket); + } + else + { + // Step though the bucket looking for a place to insert. + local_iterator inode_previous = bucket.before_begin(); + local_iterator inode = bucket.begin(); + + while (inode != bucket.end()) + { + // Do we already have this key? + if (inode->key == key) + { + break; + } + + ++inode_previous; + ++inode; + } + + // Not already there? + if (inode == bucket.end()) + { + // Get a new node. + node_t& node = *pnodepool->allocate(node_t(key)); + + // Add the node to the end of the bucket; + bucket.insert_after(inode_previous, node); + ++inode_previous; + + result.first = iterator(pbuckets->end(), ibucket, inode_previous); + result.second = true; + } + } + + return result; + } + + //********************************************************************* + /// Inserts a value to the unordered_set. + /// If asserts or exceptions are enabled, emits unordered_set_full if the unordered_set is already full. + ///\param position The position to insert at. + ///\param value The value to insert. + //********************************************************************* + iterator insert(const_iterator position, const value_type& key) + { + return insert(key).first; + } + + //********************************************************************* + /// Inserts a range of values to the unordered_set. + /// If asserts or exceptions are enabled, emits unordered_set_full if the unordered_set does not have enough free space. + ///\param position The position to insert at. + ///\param first The first element to add. + ///\param last The last + 1 element to add. + //********************************************************************* + template + void insert(TIterator first, TIterator last) + { + while (first != last) + { + insert(*first++); + } + } + + //********************************************************************* + /// Erases an element. + ///\param key The key to erase. + ///\return The number of elements erased. 0 or 1. + //********************************************************************* + size_t erase(key_value_parameter_t key) + { + size_t count = 0; + size_t bucket_id = bucket(key); + + bucket_t& bucket = (*pbuckets)[bucket_id]; + + local_iterator iprevious = bucket.before_begin(); + local_iterator icurrent = bucket.begin(); + + while ((icurrent != bucket.end()) && (icurrent->key != key)) + { + ++iprevious; + ++icurrent; + } + + if (icurrent != bucket.end()) + { + bucket.erase_after(iprevious); + count = 1; + } + + return count; + } + + //********************************************************************* + /// Erases an element. + ///\param ielement Iterator to the element. + //********************************************************************* + iterator erase(const_iterator ielement) + { + // Make a note of the next one. + iterator inext(pbuckets->end(), ielement.get_bucket_list_iterator(), ielement.get_local_iterator()); + ++inext; + + bucket_t& bucket = ielement.get_bucket(); + local_iterator icurrent = ielement.get_local_iterator(); + local_iterator iprevious = bucket.before_begin(); + + // Find the node we're interested in. + while (iprevious->etl_next != &*icurrent) + { + ++iprevious; + } + + bucket.erase_after(iprevious); + + return inext; + } + + //********************************************************************* + /// Erases a range of elements. + /// The range includes all the elements between first and last, including the + /// element pointed by first, but not the one pointed to by last. + ///\param first Iterator to the first element. + ///\param last Iterator to the last element. + //********************************************************************* + iterator erase(const_iterator first, const_iterator last) + { + // Make a note of the last. + iterator result(pbuckets->end(), last.get_bucket_list_iterator(), last.get_local_iterator()); + + // Get the starting point. + bucket_list_iterator ibucket = first.get_bucket_list_iterator(); + local_iterator ifirst = first.get_local_iterator(); + local_iterator iprevious = ibucket->before_begin(); + local_iterator iend; + + // Find the first node we're interested in. + while (iprevious->etl_next != &*ifirst) + { + ++iprevious; + } + + iend = iprevious; + iend++; + + while (first != last) + { + // Find how far we can go in this bucket. + while ((first != last) && (iend != ibucket->end())) + { + ++first; + ++iend; + } + + // Erase the range. + ibucket->erase_after(iprevious, iend); + + // At the end of this bucket? + if (iend == ibucket->end()) + { + // Move on to the next bucket. + ++ibucket; + iprevious = ibucket->before_begin(); + iend = iprevious; + ++iend; + } + else + { + // Still in the same bucket. + iprevious = iend; + } + } + + return result; + } + + //************************************************************************* + /// Clears the unordered_set. + //************************************************************************* + void clear() + { + initialise(); + } + + //********************************************************************* + /// Counts an element. + ///\param key The key to search for. + ///\return 1 if the key exists, otherwise 0. + //********************************************************************* + size_t count(key_value_parameter_t key) const + { + return (find(key) == end()) ? 0 : 1; + } + + //********************************************************************* + /// Finds an element. + ///\param key The key to search for. + ///\return An iterator to the element if the key exists, otherwise end(). + //********************************************************************* + iterator find(key_value_parameter_t key) + { + size_t index = bucket(key); + + bucket_list_iterator ibucket = pbuckets->begin() + index; + bucket_t& bucket = *ibucket; + + // Is the bucket not empty? + if (!bucket.empty()) + { + // Step though the list until we find the end or an equivalent key. + local_iterator inode = bucket.begin(); + local_iterator iend = bucket.end(); + + while (inode != iend) + { + // Do we have this one? + if (key_equal_function(key, inode->key)) + { + return iterator(pbuckets->end(), ibucket, inode); + } + + ++inode; + } + } + + return end(); + } + + //********************************************************************* + /// Finds an element. + ///\param key The key to search for. + ///\return An iterator to the element if the key exists, otherwise end(). + //********************************************************************* + const_iterator find(key_value_parameter_t key) const + { + size_t index = bucket(key); + + bucket_list_iterator ibucket = pbuckets->begin() + index; + bucket_t& bucket = *ibucket; + + // Is the bucket not empty? + if (!bucket.empty()) + { + // Step though the list until we find the end or an equivalent key. + local_iterator inode = bucket.begin(); + local_iterator iend = bucket.end(); + + while (inode != iend) + { + // Do we have this one? + if (key_equal_function(key, inode->key)) + { + return iterator(pbuckets->end(), ibucket, inode); + } + + ++inode; + } + } + + return end(); + } + + //********************************************************************* + /// Returns a range containing all elements with key 'key' in the container. + /// The range is defined by two iterators, the first pointing to the first + /// element of the wanted range and the second pointing past the last + /// element of the range. + ///\param key The key to search for. + ///\return An iterator pair to the range of elements if the key exists, otherwise end(). + //********************************************************************* + std::pair equal_range(const key_value_parameter_t& key) + { + iterator first = find(key); + iterator last = first; + + if (last != end()) + { + ++last; + } + + return std::pair(first, last); + } + + //********************************************************************* + /// Returns a range containing all elements with key 'key' in the container. + /// The range is defined by two iterators, the first pointing to the first + /// element of the wanted range and the second pointing past the last + /// element of the range. + ///\param key The key to search for. + ///\return A const iterator pair to the range of elements if the key exists, otherwise end(). + //********************************************************************* + std::pair equal_range(const key_value_parameter_t& key) const + { + const_iterator first = find(key); + const_iterator last = first; + + if (last != end()) + { + ++last; + } + + return std::pair(first, last); + } + + //************************************************************************* + /// Gets the size of the unordered_set. + //************************************************************************* + size_type size() const + { + return pnodepool->size(); + } + + //************************************************************************* + /// Gets the maximum possible size of the unordered_set. + //************************************************************************* + size_type max_size() const + { + return pnodepool->max_size(); + } + + //************************************************************************* + /// Checks to see if the unordered_set is empty. + //************************************************************************* + bool empty() const + { + return pnodepool->empty(); + } + + //************************************************************************* + /// Checks to see if the unordered_set is full. + //************************************************************************* + bool full() const + { + return pnodepool->full(); + } + + //************************************************************************* + /// Returns the remaining capacity. + ///\return The remaining capacity. + //************************************************************************* + size_t available() const + { + return pnodepool->available(); + } + + //************************************************************************* + /// Returns the load factor = size / bucket_count. + ///\return The load factor = size / bucket_count. + //************************************************************************* + float load_factor() const + { + return static_cast(size()) / static_cast(bucket_count()); + } + + //************************************************************************* + /// Returns the function that hashes the keys. + ///\return The function that hashes the keys.. + //************************************************************************* + hasher hash_function() const + { + return key_hash_function; + } + + //************************************************************************* + /// Returns the function that compares the keys. + ///\return The function that compares the keys.. + //************************************************************************* + key_equal key_eq() const + { + return key_equal_function; + } + + //************************************************************************* + /// Assignment operator. + //************************************************************************* + iunordered_set& operator = (const iunordered_set& rhs) + { + // Skip if doing self assignment + if (this != &rhs) + { + assign(rhs.cbegin(), rhs.cend()); + } + + return *this; + } + + protected: + + //********************************************************************* + /// Constructor. + //********************************************************************* + iunordered_set(pool_t& node_pool, bucket_list_t& buckets) + : pnodepool(&node_pool), + pbuckets(&buckets) + { + } + + //********************************************************************* + /// Initialise the unordered_set. + //********************************************************************* + void initialise() + { + pbuckets->resize(pnodepool->max_size()); + + if (!empty()) + { + pnodepool->release_all(); + + for (size_t i = 0; i < pbuckets->size(); ++i) + { + (*pbuckets)[i].clear(); + } + } + + first = pbuckets->begin(); + last = first; + } + + private: + + //********************************************************************* + /// Adjust the first and last markers according to the new entry. + //********************************************************************* + void adjust_first_last_markers(bucket_list_iterator ibucket) + { + if (ibucket < first) + { + first = ibucket; + } + else if (ibucket > last) + { + last = ibucket; + } + } + + // Disable copy construction. + iunordered_set(const iunordered_set&); + + /// The pool of data nodes used in the list. + pool_t* pnodepool; + + /// The bucket list. + bucket_list_t* pbuckets; + + /// The first and last iterators to buckets with values. + bucket_list_iterator first; + bucket_list_iterator last; + + /// The function that creates the hashes. + hasher key_hash_function; + + /// The function that compares the keys for equality. + key_equal key_equal_function; + }; + + //*************************************************************************** + /// Equal operator. + ///\param lhs Reference to the first unordered_set. + ///\param rhs Reference to the second unordered_set. + ///\return true if the arrays are equal, otherwise false + ///\ingroup unordered_set + //*************************************************************************** + template + bool operator ==(const etl::iunordered_set& lhs, const etl::iunordered_set& rhs) + { + return (lhs.size() == rhs.size()) && std::equal(lhs.begin(), lhs.end(), rhs.begin()); + } + + //*************************************************************************** + /// Not equal operator. + ///\param lhs Reference to the first unordered_set. + ///\param rhs Reference to the second unordered_set. + ///\return true if the arrays are not equal, otherwise false + ///\ingroup unordered_set + //*************************************************************************** + template + bool operator !=(const etl::iunordered_set& lhs, const etl::iunordered_set& rhs) + { + return !(lhs == rhs); + } +} + +#undef ETL_FILE +#endif diff --git a/test/test_unordered_map.cpp b/test/test_unordered_map.cpp index 01af4012..f39d6770 100644 --- a/test/test_unordered_map.cpp +++ b/test/test_unordered_map.cpp @@ -517,6 +517,58 @@ namespace CHECK_EQUAL(0, count); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_equal_range) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + std::pair result; + + result = data.equal_range(K0); + CHECK(result.first == data.begin()); + CHECK(result.second != data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 1); + CHECK_EQUAL(result.first->first, K0); + + result = data.equal_range(K3); + CHECK(result.first != data.begin()); + CHECK(result.second != data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 1); + CHECK_EQUAL(result.first->first, K3); + + result = data.equal_range(K9); + CHECK(result.first != data.begin()); + CHECK(result.second == data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 1); + CHECK_EQUAL(result.first->first, K9); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_equal_range_const) + { + const DataNDC data(initial_data.begin(), initial_data.end()); + + std::pair result; + + result = data.equal_range(K0); + CHECK(result.first == data.begin()); + CHECK(result.second != data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 1); + CHECK_EQUAL(result.first->first, K0); + + result = data.equal_range(K3); + CHECK(result.first != data.begin()); + CHECK(result.second != data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 1); + CHECK_EQUAL(result.first->first, K3); + + result = data.equal_range(K9); + CHECK(result.first != data.begin()); + CHECK(result.second == data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 1); + CHECK_EQUAL(result.first->first, K9); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_equal) { diff --git a/test/test_unordered_multimap.cpp b/test/test_unordered_multimap.cpp new file mode 100644 index 00000000..8f7baddc --- /dev/null +++ b/test/test_unordered_multimap.cpp @@ -0,0 +1,592 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2016 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "data.h" + +#include "../unordered_multimap.h" + +namespace +{ + //************************************************************************* + struct simple_hash + { + size_t operator ()(const std::string& text) const + { + return std::accumulate(text.begin(), text.end(), 0); + } + }; + + //************************************************************************* + template + bool Check_Equal(T1 begin1, T1 end1, T2 begin2) + { + while (begin1 != end1) + { + if ((begin1->first != begin2->first) || (begin1->second != begin2->second)) + { + return false; + } + + ++begin1; + ++begin2; + } + + return true; + } + + SUITE(test_unordered_multimap) + { + static const size_t SIZE = 10; + + typedef TestDataDC DC; + typedef TestDataNDC NDC; + + typedef std::pair ElementDC; + typedef std::pair ElementNDC; + + typedef etl::unordered_multimap DataDC; + typedef etl::unordered_multimap DataNDC; + typedef etl::iunordered_multimap IDataNDC; + + NDC N0 = NDC("A"); + NDC N1 = NDC("B"); + NDC N2 = NDC("C"); + NDC N3 = NDC("D"); + NDC N4 = NDC("E"); + NDC N5 = NDC("F"); + NDC N6 = NDC("G"); + NDC N7 = NDC("H"); + NDC N8 = NDC("I"); + NDC N9 = NDC("J"); + NDC N10 = NDC("K"); + NDC N11 = NDC("L"); + NDC N12 = NDC("M"); + NDC N13 = NDC("N"); + NDC N14 = NDC("O"); + NDC N15 = NDC("P"); + NDC N16 = NDC("Q"); + NDC N17 = NDC("R"); + NDC N18 = NDC("S"); + NDC N19 = NDC("T"); + + const char* K0 = "FF"; // 0 + const char* K1 = "FG"; // 1 + const char* K2 = "FH"; // 2 + const char* K3 = "FI"; // 3 + const char* K4 = "FJ"; // 4 + const char* K5 = "FK"; // 5 + const char* K6 = "FL"; // 6 + const char* K7 = "FM"; // 7 + const char* K8 = "FN"; // 8 + const char* K9 = "FO"; // 9 + const char* K10 = "FP"; // 0 + const char* K11 = "FQ"; // 1 + const char* K12 = "FR"; // 2 + const char* K13 = "FS"; // 3 + const char* K14 = "FT"; // 4 + const char* K15 = "FU"; // 5 + const char* K16 = "FV"; // 6 + const char* K17 = "FW"; // 7 + const char* K18 = "FX"; // 8 + const char* K19 = "FY"; // 9 + + std::string K[] = { K0, K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, K12, K13, K14, K15, K16, K17, K18, K19 }; + + std::vector initial_data; + std::vector excess_data; + std::vector different_data; + std::vector equal_data; + + //************************************************************************* + template + bool Check_Equal(T1 begin1, T1 end1, T2 begin2) + { + while (begin1 != end1) + { + if ((begin1->first != begin2->first) || (begin1->second != begin2->second)) + { + return false; + } + + ++begin1; + ++begin2; + } + + return true; + } + + //************************************************************************* + struct SetupFixture + { + SetupFixture() + { + ElementNDC n[] = + { + ElementNDC(K0, N0), ElementNDC(K1, N1), ElementNDC(K2, N2), ElementNDC(K3, N3), ElementNDC(K4, N4), + ElementNDC(K5, N5), ElementNDC(K6, N6), ElementNDC(K7, N7), ElementNDC(K8, N8), ElementNDC(K9, N9) + }; + + ElementNDC n2[] = + { + ElementNDC(K0, N0), ElementNDC(K1, N1), ElementNDC(K2, N2), ElementNDC(K3, N3), ElementNDC(K4, N4), + ElementNDC(K5, N5), ElementNDC(K6, N6), ElementNDC(K7, N7), ElementNDC(K8, N8), ElementNDC(K9, N9), + ElementNDC(K10, N10) + }; + + ElementNDC n3[] = + { + ElementNDC(K10, N10), ElementNDC(K11, N11), ElementNDC(K12, N12), ElementNDC(K13, N13), ElementNDC(K14, N14), + ElementNDC(K15, N15), ElementNDC(K16, N16), ElementNDC(K17, N17), ElementNDC(K18, N18), ElementNDC(K19, N19) + }; + + ElementNDC n4[] = + { + ElementNDC(K10, N10), ElementNDC(K11, N11), ElementNDC(K11, N12), ElementNDC(K11, N13), ElementNDC(K12, N14) + }; + + initial_data.assign(std::begin(n), std::end(n)); + excess_data.assign(std::begin(n2), std::end(n2)); + different_data.assign(std::begin(n3), std::end(n3)); + equal_data.assign(std::begin(n4), std::end(n4)); + } + }; + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_default_constructor) + { + DataDC data; + + CHECK_EQUAL(data.size(), size_t(0)); + CHECK(data.empty()); + CHECK_EQUAL(data.max_size(), SIZE); + CHECK(data.begin() == data.end()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_range) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + CHECK(data.size() == SIZE); + CHECK(!data.empty()); + CHECK(data.full()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_assignment) + { + DataNDC data(initial_data.begin(), initial_data.end()); + DataNDC other_data; + + other_data = data; + + bool isEqual = std::equal(data.begin(), + data.end(), + other_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_assignment_interface) + { + DataNDC data1(initial_data.begin(), initial_data.end()); + DataNDC data2; + + IDataNDC& idata1 = data1; + IDataNDC& idata2 = data2; + + idata2 = idata1; + + bool isEqual = std::equal(data1.begin(), + data1.end(), + data2.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_self_assignment) + { + DataNDC data(initial_data.begin(), initial_data.end()); + DataNDC other_data(data); + + other_data = other_data; + + bool isEqual = std::equal(data.begin(), + data.end(), + other_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_empty_full) + { + DataNDC data; + + CHECK(!data.full()); + CHECK(data.empty()); + + data.insert(initial_data.begin(), initial_data.end()); + + CHECK(data.full()); + CHECK(!data.empty()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_assign_range) + { + DataNDC data; + + data.assign(initial_data.begin(), initial_data.end()); + + DataNDC::iterator idata; + + for (size_t i = 0; i < 10; ++i) + { + idata = data.find(K[i]); + CHECK(idata != data.end()); + } + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_value) + { + DataNDC data; + + data.insert(DataNDC::value_type(K0, N0)); // Inserted + data.insert(DataNDC::value_type(K2, N2)); // Inserted + data.insert(DataNDC::value_type(K1, N1)); // Inserted + data.insert(DataNDC::value_type(K11, N1)); // Duplicate hash. Inserted + data.insert(DataNDC::value_type(K1, N3)); // Duplicate key. Inserted + + CHECK_EQUAL(5U, data.size()); + + DataNDC::iterator idata; + + idata = data.find(K0); + + std::string k = idata->first; + NDC n = idata->second; + + CHECK(idata != data.end()); + CHECK(idata->first == K0); + CHECK(idata->second == N0); + + idata = data.find(K1); + CHECK(idata != data.end()); + CHECK(idata->first == K1); + CHECK(idata->second == N3); + + // The other value with key == K1 + ++idata; + CHECK(idata != data.end()); + CHECK(idata->first == K1); + CHECK(idata->second == N1); + + idata = data.find(K2); + CHECK(idata != data.end()); + CHECK(idata->first == K2); + CHECK(idata->second == N2); + + idata = data.find(K11); + CHECK(idata != data.end()); + CHECK(idata->first == K11); + CHECK(idata->second == N1); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_value_excess) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + CHECK_THROW(data.insert(std::make_pair(K10, N10)), etl::unordered_multimap_full); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_range) + { + DataNDC data; + + data.insert(initial_data.begin(), initial_data.end()); + + for (size_t i = 0; i < data.size(); ++i) + { + DataNDC::iterator idata = data.find(initial_data[i].first); + CHECK(idata != data.end()); + } + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_range_excess) + { + DataNDC data; + + CHECK_THROW(data.insert(excess_data.begin(), excess_data.end()), etl::unordered_multimap_full); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_key) + { + DataNDC data(equal_data.begin(), equal_data.end()); + + size_t count = data.erase(K10); + + CHECK_EQUAL(1, count); + + DataNDC::iterator idata = data.find(K10); + CHECK(idata == data.end()); + + count = data.erase(K11); + + CHECK_EQUAL(3, count); + + idata = data.find(K11); + CHECK(idata == data.end()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_single) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + DataNDC::const_iterator idata = data.find(K5); + DataNDC::const_iterator inext = idata; + ++inext; + + DataNDC::const_iterator iafter = data.erase(idata); + idata = data.find(K5); + + CHECK(idata == data.end()); + CHECK(inext == iafter); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_range) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + DataNDC::iterator idata = data.find(K5); + DataNDC::iterator idata_end = data.find(K8); + + idata = data.erase(idata, idata_end); // Erase K5, K6, K7 + CHECK(idata == data.find(K8)); + + idata = data.find(K0); + CHECK(idata != data.end()); + + idata = data.find(K1); + CHECK(idata != data.end()); + + idata = data.find(K2); + CHECK(idata != data.end()); + + idata = data.find(K3); + CHECK(idata != data.end()); + + idata = data.find(K4); + CHECK(idata != data.end()); + + idata = data.find(K5); + CHECK(idata == data.end()); + + idata = data.find(K6); + CHECK(idata == data.end()); + + idata = data.find(K7); + CHECK(idata == data.end()); + + idata = data.find(K8); + CHECK(idata != data.end()); + + idata = data.find(K9); + CHECK(idata != data.end()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_clear) + { + DataNDC data(initial_data.begin(), initial_data.end()); + data.clear(); + + CHECK_EQUAL(data.size(), size_t(0)); + } + + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_count_key) + { + DataNDC data(equal_data.begin(), equal_data.end()); + + size_t count = data.count(K10); + CHECK_EQUAL(1, count); + + count = data.count(K11); + CHECK_EQUAL(3, count); + + count = data.count(K1); + CHECK_EQUAL(0, count); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_find_const) + { + const DataNDC data(initial_data.begin(), initial_data.end()); + + DataNDC::const_iterator idata = data.find(K3); + + CHECK(idata != data.end()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_equal_range) + { + DataNDC data(equal_data.begin(), equal_data.end()); + + std::pair result; + + result = data.equal_range(K10); + CHECK(result.first == data.begin()); + CHECK(result.second != data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 1); + CHECK_EQUAL(result.first->first, K10); + + result = data.equal_range(K11); + CHECK(result.first != data.begin()); + CHECK(result.second != data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 3); + CHECK_EQUAL(result.first->first, K11); + + result = data.equal_range(K12); + CHECK(result.first != data.begin()); + CHECK(result.second == data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 1); + CHECK_EQUAL(result.first->first, K12); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_equal_range_const) + { + const DataNDC data(equal_data.begin(), equal_data.end()); + + std::pair result; + + result = data.equal_range(K10); + CHECK(result.first == data.begin()); + CHECK(result.second != data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 1); + CHECK_EQUAL(result.first->first, K10); + + result = data.equal_range(K11); + CHECK(result.first != data.begin()); + CHECK(result.second != data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 3); + CHECK_EQUAL(result.first->first, K11); + + result = data.equal_range(K12); + CHECK(result.first != data.begin()); + CHECK(result.second == data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 1); + CHECK_EQUAL(result.first->first, K12); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_equal) + { + const DataNDC initial1(initial_data.begin(), initial_data.end()); + const DataNDC initial2(initial_data.begin(), initial_data.end()); + + CHECK(initial1 == initial2); + + const DataNDC different(different_data.begin(), different_data.end()); + + CHECK(!(initial1 == different)); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_not_equal) + { + const DataNDC initial1(initial_data.begin(), initial_data.end()); + const DataNDC initial2(initial_data.begin(), initial_data.end()); + + CHECK(!(initial1 != initial2)); + + const DataNDC different(different_data.begin(), different_data.end()); + + CHECK(initial1 != different); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_hash_function) + { + DataNDC data; + DataNDC::hasher hash_function = data.hash_function(); + + CHECK_EQUAL(simple_hash()(std::string("ABCDEF")), hash_function(std::string("ABCDEF"))); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_key_eq_function) + { + DataNDC data; + DataNDC::key_equal key_eq = data.key_eq(); + + CHECK(key_eq(std::string("ABCDEF"), std::string("ABCDEF"))); + CHECK(!key_eq(std::string("ABCDEF"), std::string("ABCDEG"))); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_load_factor) + { + // Empty. + DataNDC data; + CHECK_CLOSE(0.0, data.load_factor(), 0.01); + + // Half the buckets used. + data.assign(initial_data.begin(), initial_data.begin() + (initial_data.size() / 2)); + CHECK_CLOSE(0.5, data.load_factor(), 0.01); + + // All of the buckets used. + data.clear(); + data.assign(initial_data.begin(), initial_data.end()); + CHECK_CLOSE(1.0, data.load_factor(), 0.01); + } + }; +} diff --git a/test/test_unordered_multiset.cpp b/test/test_unordered_multiset.cpp new file mode 100644 index 00000000..e4b95fc6 --- /dev/null +++ b/test/test_unordered_multiset.cpp @@ -0,0 +1,522 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2016 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "data.h" + +#include "../unordered_multiset.h" +#include "../checksum.h" + +namespace +{ + SUITE(test_unordered_multiset) + { + static const size_t SIZE = 10; + + typedef TestDataDC DC; + typedef TestDataNDC NDC; + + struct simple_hash + { + size_t operator ()(const NDC& value) const + { + return etl::checksum(value.value.begin(), value.value.end()); + } + }; + + typedef etl::unordered_multiset DataDC; + typedef etl::unordered_multiset DataNDC; + typedef etl::iunordered_multiset IDataNDC; + + NDC N0 = NDC("FF"); + NDC N1 = NDC("FG"); + NDC N2 = NDC("FH"); + NDC N3 = NDC("FI"); + NDC N4 = NDC("FJ"); + NDC N5 = NDC("FK"); + NDC N6 = NDC("FL"); + NDC N7 = NDC("FM"); + NDC N8 = NDC("FN"); + NDC N9 = NDC("FO"); + NDC N10 = NDC("FP"); + NDC N11 = NDC("FQ"); + NDC N12 = NDC("FR"); + NDC N13 = NDC("FS"); + NDC N14 = NDC("FT"); + NDC N15 = NDC("FU"); + NDC N16 = NDC("FV"); + NDC N17 = NDC("FW"); + NDC N18 = NDC("FX"); + NDC N19 = NDC("FY"); + + std::vector initial_data; + std::vector excess_data; + std::vector different_data; + std::vector equal_data; + + //************************************************************************* + struct SetupFixture + { + SetupFixture() + { + NDC n[] = + { + N0, N1, N2, N3, N4, N5, N6, N7, N8, N9 + }; + + NDC n2[] = + { + N0, N1, N2, N3, N4, N5, N6, N7, N8, N9, N10 + }; + + NDC n3[] = + { + N10, N11, N12, N13, N14, N15, N16, N17, N18, N19 + }; + + NDC n4[] = + { + N0, N1, N1, N1, N2 + }; + + initial_data.assign(std::begin(n), std::end(n)); + excess_data.assign(std::begin(n2), std::end(n2)); + different_data.assign(std::begin(n3), std::end(n3)); + equal_data.assign(std::begin(n4), std::end(n4)); + } + }; + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_default_constructor) + { + DataDC data; + + CHECK_EQUAL(data.size(), size_t(0)); + CHECK(data.empty()); + CHECK_EQUAL(data.max_size(), SIZE); + CHECK(data.begin() == data.end()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_range) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + CHECK(data.size() == SIZE); + CHECK(!data.empty()); + CHECK(data.full()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_assignment) + { + DataNDC data(initial_data.begin(), initial_data.end()); + DataNDC other_data; + + other_data = data; + + bool isEqual = std::equal(data.begin(), + data.end(), + other_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_assignment_interface) + { + DataNDC data1(initial_data.begin(), initial_data.end()); + DataNDC data2; + + IDataNDC& idata1 = data1; + IDataNDC& idata2 = data2; + + idata2 = idata1; + + bool isEqual = std::equal(data1.begin(), + data1.end(), + data2.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_self_assignment) + { + DataNDC data(initial_data.begin(), initial_data.end()); + DataNDC other_data(data); + + other_data = other_data; + + bool isEqual = std::equal(data.begin(), + data.end(), + other_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_empty_full) + { + DataNDC data; + + CHECK(!data.full()); + CHECK(data.empty()); + + data.insert(initial_data.begin(), initial_data.end()); + + CHECK(data.full()); + CHECK(!data.empty()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_assign_range) + { + DataNDC data; + + data.assign(initial_data.begin(), initial_data.end()); + + DataNDC::iterator idata; + + for (size_t i = 0; i < 10; ++i) + { + idata = data.find(initial_data[i]); + CHECK(idata != data.end()); + } + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_value) + { + DataNDC data; + + data.insert(N0); // Inserted + data.insert(N2); // Inserted + data.insert(N1); // Inserted + data.insert(N11); // Duplicate hash. Inserted + data.insert(N1); // Duplicate. Inserted + + CHECK_EQUAL(5U, data.size()); + + DataNDC::iterator idata; + + idata = data.find(N0); + CHECK(idata != data.end()); + CHECK(*idata == N0); + + idata = data.find(N1); + CHECK(idata != data.end()); + CHECK(*idata == N1); + + // The other value with key == N1 + ++idata; + CHECK(idata != data.end()); + CHECK(*idata == N1); + + idata = data.find(N2); + CHECK(idata != data.end()); + CHECK(*idata == N2); + + idata = data.find(N11); + CHECK(idata != data.end()); + CHECK(*idata == N11); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_value_excess) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + CHECK_THROW(data.insert(N10), etl::unordered_multiset_full); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_range) + { + DataNDC data; + + data.insert(initial_data.begin(), initial_data.end()); + + for (size_t i = 0; i < data.size(); ++i) + { + DataNDC::iterator idata = data.find(initial_data[i]); + CHECK(idata != data.end()); + } + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_range_excess) + { + DataNDC data; + + CHECK_THROW(data.insert(excess_data.begin(), excess_data.end()), etl::unordered_multiset_full); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_key) + { + DataNDC data(equal_data.begin(), equal_data.end()); + + size_t count = data.erase(N0); + + CHECK_EQUAL(1, count); + + DataNDC::iterator idata = data.find(N0); + CHECK(idata == data.end()); + + count = data.erase(N1); + + CHECK_EQUAL(3, count); + + idata = data.find(N1); + CHECK(idata == data.end()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_single) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + DataNDC::const_iterator idata = data.find(N5); + DataNDC::const_iterator inext = idata; + ++inext; + + DataNDC::const_iterator iafter = data.erase(idata); + idata = data.find(N5); + + CHECK(idata == data.end()); + CHECK(inext == iafter); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_range) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + DataNDC::iterator idata = data.find(N5); + DataNDC::iterator idata_end = data.find(N8); + + std::vector test; + + test.assign(data.begin(), data.end()); + + idata = data.erase(idata, idata_end); // Erase N5, N6, N7 + CHECK(idata == data.find(N8)); + + test.assign(data.begin(), data.end()); + + idata = data.find(N0); + CHECK(idata != data.end()); + + idata = data.find(N1); + CHECK(idata != data.end()); + + idata = data.find(N2); + CHECK(idata != data.end()); + + idata = data.find(N3); + CHECK(idata != data.end()); + + idata = data.find(N4); + CHECK(idata != data.end()); + + idata = data.find(N5); + CHECK(idata == data.end()); + + idata = data.find(N6); + CHECK(idata == data.end()); + + idata = data.find(N7); + CHECK(idata == data.end()); + + idata = data.find(N8); + CHECK(idata != data.end()); + + idata = data.find(N9); + CHECK(idata != data.end()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_clear) + { + DataNDC data(initial_data.begin(), initial_data.end()); + data.clear(); + + CHECK_EQUAL(data.size(), size_t(0)); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_count_key) + { + DataNDC data(equal_data.begin(), equal_data.end()); + + size_t count = data.count(N0); + CHECK_EQUAL(1, count); + + count = data.count(N1); + CHECK_EQUAL(3, count); + + count = data.count(N10); + CHECK_EQUAL(0, count); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_find_const) + { + const DataNDC data(initial_data.begin(), initial_data.end()); + + DataNDC::const_iterator idata = data.find(N3); + + CHECK(idata != data.end()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_equal_range) + { + DataNDC data(equal_data.begin(), equal_data.end()); + + std::pair result; + + result = data.equal_range(N0); + CHECK(result.first == data.begin()); + CHECK(result.second != data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 1); + CHECK_EQUAL(*result.first, N0); + + result = data.equal_range(N1); + CHECK(result.first != data.begin()); + CHECK(result.second != data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 3); + CHECK_EQUAL(*result.first, N1); + + result = data.equal_range(N2); + CHECK(result.first != data.begin()); + CHECK(result.second == data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 1); + CHECK_EQUAL(*result.first, N2); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_equal_range_const) + { + const DataNDC data(equal_data.begin(), equal_data.end()); + + std::pair result; + + result = data.equal_range(N0); + CHECK(result.first == data.begin()); + CHECK(result.second != data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 1); + CHECK_EQUAL(*result.first, N0); + + result = data.equal_range(N1); + CHECK(result.first != data.begin()); + CHECK(result.second != data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 3); + CHECK_EQUAL(*result.first, N1); + + result = data.equal_range(N2); + CHECK(result.first != data.begin()); + CHECK(result.second == data.end()); + CHECK_EQUAL(std::distance(result.first, result.second), 1); + CHECK_EQUAL(*result.first, N2); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_equal) + { + const DataNDC initial1(initial_data.begin(), initial_data.end()); + const DataNDC initial2(initial_data.begin(), initial_data.end()); + + CHECK(initial1 == initial2); + + const DataNDC different(different_data.begin(), different_data.end()); + + CHECK(!(initial1 == different)); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_not_equal) + { + const DataNDC initial1(initial_data.begin(), initial_data.end()); + const DataNDC initial2(initial_data.begin(), initial_data.end()); + + CHECK(!(initial1 != initial2)); + + const DataNDC different(different_data.begin(), different_data.end()); + + CHECK(initial1 != different); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_hash_function) + { + DataNDC data; + DataNDC::hasher hash_function = data.hash_function(); + + CHECK_EQUAL(simple_hash()(std::string("ABCDEF")), hash_function(std::string("ABCDEF"))); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_key_eq_function) + { + DataNDC data; + DataNDC::key_equal key_eq = data.key_eq(); + + CHECK(key_eq(std::string("ABCDEF"), std::string("ABCDEF"))); + CHECK(!key_eq(std::string("ABCDEF"), std::string("ABCDEG"))); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_load_factor) + { + // Empty. + DataNDC data; + CHECK_CLOSE(0.0, data.load_factor(), 0.01); + + // Half the buckets used. + data.assign(initial_data.begin(), initial_data.begin() + (initial_data.size() / 2)); + CHECK_CLOSE(0.5, data.load_factor(), 0.01); + + // All of the buckets used. + data.clear(); + data.assign(initial_data.begin(), initial_data.end()); + CHECK_CLOSE(1.0, data.load_factor(), 0.01); + } + }; +} diff --git a/test/vs2015/etl.vcxproj b/test/vs2015/etl.vcxproj index 48c7ee62..9beecfae 100644 --- a/test/vs2015/etl.vcxproj +++ b/test/vs2015/etl.vcxproj @@ -215,6 +215,8 @@ + + @@ -263,6 +265,7 @@ + @@ -368,6 +371,8 @@ + + diff --git a/test/vs2015/etl.vcxproj.filters b/test/vs2015/etl.vcxproj.filters index 16b2425d..5d8d1586 100644 --- a/test/vs2015/etl.vcxproj.filters +++ b/test/vs2015/etl.vcxproj.filters @@ -468,6 +468,15 @@ ETL\Containers + + ETL\Containers + + + ETL\Containers + + + ETL\Containers + @@ -725,6 +734,12 @@ Source Files + + Source Files + + + Source Files + diff --git a/unordered_multimap.h b/unordered_multimap.h new file mode 100644 index 00000000..14746fee --- /dev/null +++ b/unordered_multimap.h @@ -0,0 +1,123 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2016 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_UNORDERED_MULTIMAP__ +#define __ETL_UNORDERED_MULTIMAP__ + +#include +#include +#include + +#include "iunordered_multimap.h" +#include "container.h" +#include "pool.h" +#include "vector.h" +#include "intrusive_forward_list.h" +#include "hash.h" + +//***************************************************************************** +///\defgroup unordered_multimap unordered_multimap +/// A unordered_multimap with the capacity defined at compile time. +///\ingroup containers +//***************************************************************************** + +namespace etl +{ + //************************************************************************* + /// A templated unordered_multimap implementation that uses a fixed size buffer. + //************************************************************************* + template , typename TKeyEqual = std::equal_to > + class unordered_multimap : public iunordered_multimap + { + private: + + typedef iunordered_multimap base; + + public: + + static const size_t MAX_SIZE = MAX_SIZE_; + + //************************************************************************* + /// Default constructor. + //************************************************************************* + unordered_multimap() + : base(node_pool, buckets) + { + base::initialise(); + } + + //************************************************************************* + /// Copy constructor. + //************************************************************************* + unordered_multimap(const unordered_multimap& other) + : base(node_pool, buckets) + { + base::assign(other.cbegin(), other.cend()); + } + + //************************************************************************* + /// Constructor, from an iterator range. + ///\tparam TIterator The iterator type. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + //************************************************************************* + template + unordered_multimap(TIterator first, TIterator last) + : base(node_pool, buckets) + { + base::assign(first, last); + } + + //************************************************************************* + /// Assignment operator. + //************************************************************************* + unordered_multimap& operator = (const unordered_multimap& rhs) + { + // Skip if doing self assignment + if (this != &rhs) + { + base::assign(rhs.cbegin(), rhs.cend()); + } + + return *this; + } + + private: + + /// The pool of nodes used for the unordered_multimap. + etl::pool node_pool; + + /// The buckets of node lists. + etl::vector, MAX_SIZE> buckets; + }; + +} + +#endif diff --git a/unordered_multiset.h b/unordered_multiset.h new file mode 100644 index 00000000..c4e1ce37 --- /dev/null +++ b/unordered_multiset.h @@ -0,0 +1,123 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2016 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_UNORDERED_MULTISET__ +#define __ETL_UNORDERED_MULTISET__ + +#include +#include +#include + +#include "iunordered_multiset.h" +#include "container.h" +#include "pool.h" +#include "vector.h" +#include "intrusive_forward_list.h" +#include "hash.h" + +//***************************************************************************** +///\defgroup unordered_multiset unordered_multiset +/// A unordered_multiset with the capacity defined at compile time. +///\ingroup containers +//***************************************************************************** + +namespace etl +{ + //************************************************************************* + /// A templated unordered_multiset implementation that uses a fixed size buffer. + //************************************************************************* + template , typename TKeyEqual = std::equal_to > + class unordered_multiset : public iunordered_multiset + { + private: + + typedef iunordered_multiset base; + + public: + + static const size_t MAX_SIZE = MAX_SIZE_; + + //************************************************************************* + /// Default constructor. + //************************************************************************* + unordered_multiset() + : base(node_pool, buckets) + { + base::initialise(); + } + + //************************************************************************* + /// Copy constructor. + //************************************************************************* + unordered_multiset(const unordered_multiset& other) + : base(node_pool, buckets) + { + base::assign(other.cbegin(), other.cend()); + } + + //************************************************************************* + /// Constructor, from an iterator range. + ///\tparam TIterator The iterator type. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + //************************************************************************* + template + unordered_multiset(TIterator first, TIterator last) + : base(node_pool, buckets) + { + base::assign(first, last); + } + + //************************************************************************* + /// Assignment operator. + //************************************************************************* + unordered_multiset& operator = (const unordered_multiset& rhs) + { + // Skip if doing self assignment + if (this != &rhs) + { + base::assign(rhs.cbegin(), rhs.cend()); + } + + return *this; + } + + private: + + /// The pool of nodes used for the unordered_multiset. + etl::pool node_pool; + + /// The buckets of node lists. + etl::vector, MAX_SIZE> buckets; + }; + +} + +#endif