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