/////////////////////////////////////////////////////////////////////////////// // Copyright (c) Electronic Arts Inc. All rights reserved. ////////////////////////////////////////////////////////////////////////////// #ifndef EASTL_MAP_H #define EASTL_MAP_H #include #include #include #include #if defined(EA_PRAGMA_ONCE_SUPPORTED) #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. #endif namespace eastl { /// EASTL_MAP_DEFAULT_NAME /// /// Defines a default container name in the absence of a user-provided name. /// #ifndef EASTL_MAP_DEFAULT_NAME #define EASTL_MAP_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " map" // Unless the user overrides something, this is "EASTL map". #endif /// EASTL_MULTIMAP_DEFAULT_NAME /// /// Defines a default container name in the absence of a user-provided name. /// #ifndef EASTL_MULTIMAP_DEFAULT_NAME #define EASTL_MULTIMAP_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " multimap" // Unless the user overrides something, this is "EASTL multimap". #endif /// EASTL_MAP_DEFAULT_ALLOCATOR /// #ifndef EASTL_MAP_DEFAULT_ALLOCATOR #define EASTL_MAP_DEFAULT_ALLOCATOR allocator_type(EASTL_MAP_DEFAULT_NAME) #endif /// EASTL_MULTIMAP_DEFAULT_ALLOCATOR /// #ifndef EASTL_MULTIMAP_DEFAULT_ALLOCATOR #define EASTL_MULTIMAP_DEFAULT_ALLOCATOR allocator_type(EASTL_MULTIMAP_DEFAULT_NAME) #endif /// map /// /// Implements a canonical map. /// /// The large majority of the implementation of this class is found in the rbtree /// base class. We control the behaviour of rbtree via template parameters. /// /// Pool allocation /// If you want to make a custom memory pool for a map container, your pool /// needs to contain items of type map::node_type. So if you have a memory /// pool that has a constructor that takes the size of pool items and the /// count of pool items, you would do this (assuming that MemoryPool implements /// the Allocator interface): /// typedef map, MemoryPool> WidgetMap; // Delare your WidgetMap type. /// MemoryPool myPool(sizeof(WidgetMap::node_type), 100); // Make a pool of 100 Widget nodes. /// WidgetMap myMap(&myPool); // Create a map that uses the pool. /// template , typename Allocator = EASTLAllocatorType> class map : public rbtree, Compare, Allocator, eastl::use_first >, true, true> { public: typedef rbtree, Compare, Allocator, eastl::use_first >, true, true> base_type; typedef map this_type; typedef typename base_type::size_type size_type; typedef typename base_type::key_type key_type; typedef T mapped_type; typedef typename base_type::value_type value_type; typedef typename base_type::node_type node_type; typedef typename base_type::iterator iterator; typedef typename base_type::const_iterator const_iterator; typedef typename base_type::allocator_type allocator_type; typedef typename base_type::insert_return_type insert_return_type; typedef typename base_type::extract_key extract_key; // Other types are inherited from the base class. using base_type::begin; using base_type::end; using base_type::find; using base_type::lower_bound; using base_type::upper_bound; using base_type::insert; using base_type::erase; protected: using base_type::compare; using base_type::get_compare; public: class value_compare { protected: friend class map; Compare compare; value_compare(Compare c) : compare(c) {} public: typedef bool result_type; typedef value_type first_argument_type; typedef value_type second_argument_type; bool operator()(const value_type& x, const value_type& y) const { return compare(x.first, y.first); } }; public: map(const allocator_type& allocator = EASTL_MAP_DEFAULT_ALLOCATOR); map(const Compare& compare, const allocator_type& allocator = EASTL_MAP_DEFAULT_ALLOCATOR); map(const this_type& x); map(this_type&& x); map(this_type&& x, const allocator_type& allocator); map(std::initializer_list ilist, const Compare& compare = Compare(), const allocator_type& allocator = EASTL_MAP_DEFAULT_ALLOCATOR); template map(Iterator itBegin, Iterator itEnd); // allocator arg removed because VC7.1 fails on the default arg. To consider: Make a second version of this function without a default arg. this_type& operator=(const this_type& x) { return (this_type&)base_type::operator=(x); } this_type& operator=(std::initializer_list ilist) { return (this_type&)base_type::operator=(ilist); } this_type& operator=(this_type&& x) { return (this_type&)base_type::operator=(eastl::move(x)); } public: /// This is an extension to the C++ standard. We insert a default-constructed /// element with the given key. The reason for this is that we can avoid the /// potentially expensive operation of creating and/or copying a mapped_type /// object on the stack. Note that C++11 move insertions and variadic emplace /// support make this extension mostly no longer necessary. insert_return_type insert(const Key& key); value_compare value_comp() const; size_type erase(const Key& key); size_type count(const Key& key) const; eastl::pair equal_range(const Key& key); eastl::pair equal_range(const Key& key) const; T& operator[](const Key& key); // Of map, multimap, set, and multimap, only map has operator[]. T& operator[](Key&& key); T& at(const Key& key); const T& at(const Key& key) const; }; // map /// multimap /// /// Implements a canonical multimap. /// /// The large majority of the implementation of this class is found in the rbtree /// base class. We control the behaviour of rbtree via template parameters. /// /// Pool allocation /// If you want to make a custom memory pool for a multimap container, your pool /// needs to contain items of type multimap::node_type. So if you have a memory /// pool that has a constructor that takes the size of pool items and the /// count of pool items, you would do this (assuming that MemoryPool implements /// the Allocator interface): /// typedef multimap, MemoryPool> WidgetMap; // Delare your WidgetMap type. /// MemoryPool myPool(sizeof(WidgetMap::node_type), 100); // Make a pool of 100 Widget nodes. /// WidgetMap myMap(&myPool); // Create a map that uses the pool. /// template , typename Allocator = EASTLAllocatorType> class multimap : public rbtree, Compare, Allocator, eastl::use_first >, true, false> { public: typedef rbtree, Compare, Allocator, eastl::use_first >, true, false> base_type; typedef multimap this_type; typedef typename base_type::size_type size_type; typedef typename base_type::key_type key_type; typedef T mapped_type; typedef typename base_type::value_type value_type; typedef typename base_type::node_type node_type; typedef typename base_type::iterator iterator; typedef typename base_type::const_iterator const_iterator; typedef typename base_type::allocator_type allocator_type; typedef typename base_type::insert_return_type insert_return_type; typedef typename base_type::extract_key extract_key; // Other types are inherited from the base class. using base_type::begin; using base_type::end; using base_type::find; using base_type::lower_bound; using base_type::upper_bound; using base_type::insert; using base_type::erase; protected: using base_type::compare; using base_type::get_compare; public: class value_compare { protected: friend class multimap; Compare compare; value_compare(Compare c) : compare(c) {} public: typedef bool result_type; typedef value_type first_argument_type; typedef value_type second_argument_type; bool operator()(const value_type& x, const value_type& y) const { return compare(x.first, y.first); } }; public: multimap(const allocator_type& allocator = EASTL_MULTIMAP_DEFAULT_ALLOCATOR); multimap(const Compare& compare, const allocator_type& allocator = EASTL_MULTIMAP_DEFAULT_ALLOCATOR); multimap(const this_type& x); multimap(this_type&& x); multimap(this_type&& x, const allocator_type& allocator); multimap(std::initializer_list ilist, const Compare& compare = Compare(), const allocator_type& allocator = EASTL_MULTIMAP_DEFAULT_ALLOCATOR); template multimap(Iterator itBegin, Iterator itEnd); // allocator arg removed because VC7.1 fails on the default arg. To consider: Make a second version of this function without a default arg. this_type& operator=(const this_type& x) { return (this_type&)base_type::operator=(x); } this_type& operator=(std::initializer_list ilist) { return (this_type&)base_type::operator=(ilist); } this_type& operator=(this_type&& x) { return (this_type&)base_type::operator=(eastl::move(x)); } public: /// This is an extension to the C++ standard. We insert a default-constructed /// element with the given key. The reason for this is that we can avoid the /// potentially expensive operation of creating and/or copying a mapped_type /// object on the stack. Note that C++11 move insertions and variadic emplace /// support make this extension mostly no longer necessary. insert_return_type insert(const Key& key); value_compare value_comp() const; size_type erase(const Key& key); size_type count(const Key& key) const; eastl::pair equal_range(const Key& key); eastl::pair equal_range(const Key& key) const; /// equal_range_small /// This is a special version of equal_range which is optimized for the /// case of there being few or no duplicated keys in the tree. eastl::pair equal_range_small(const Key& key); eastl::pair equal_range_small(const Key& key) const; private: // these base member functions are not included in multimaps using base_type::try_emplace; using base_type::insert_or_assign; }; // multimap /////////////////////////////////////////////////////////////////////// // map /////////////////////////////////////////////////////////////////////// template inline map::map(const allocator_type& allocator) : base_type(allocator) { } template inline map::map(const Compare& compare, const allocator_type& allocator) : base_type(compare, allocator) { } template inline map::map(const this_type& x) : base_type(x) { } template inline map::map(this_type&& x) : base_type(eastl::move(x)) { } template inline map::map(this_type&& x, const allocator_type& allocator) : base_type(eastl::move(x), allocator) { } template inline map::map(std::initializer_list ilist, const Compare& compare, const allocator_type& allocator) : base_type(ilist.begin(), ilist.end(), compare, allocator) { } template template inline map::map(Iterator itBegin, Iterator itEnd) : base_type(itBegin, itEnd, Compare(), EASTL_MAP_DEFAULT_ALLOCATOR) { } template inline typename map::insert_return_type map::insert(const Key& key) { return base_type::DoInsertKey(true_type(), key); } template inline typename map::value_compare map::value_comp() const { return value_compare(get_compare()); } template inline typename map::size_type map::erase(const Key& key) { const iterator it(find(key)); if(it != end()) // If it exists... { base_type::erase(it); return 1; } return 0; } template inline typename map::size_type map::count(const Key& key) const { const const_iterator it(find(key)); return (it != end()) ? 1 : 0; } template inline eastl::pair::iterator, typename map::iterator> map::equal_range(const Key& key) { // The resulting range will either be empty or have one element, // so instead of doing two tree searches (one for lower_bound and // one for upper_bound), we do just lower_bound and see if the // result is a range of size zero or one. const iterator itLower(lower_bound(key)); if((itLower == end()) || compare(key, itLower.mpNode->mValue.first)) // If at the end or if (key is < itLower)... return eastl::pair(itLower, itLower); iterator itUpper(itLower); return eastl::pair(itLower, ++itUpper); } template inline eastl::pair::const_iterator, typename map::const_iterator> map::equal_range(const Key& key) const { // See equal_range above for comments. const const_iterator itLower(lower_bound(key)); if((itLower == end()) || compare(key, itLower.mpNode->mValue.first)) // If at the end or if (key is < itLower)... return eastl::pair(itLower, itLower); const_iterator itUpper(itLower); return eastl::pair(itLower, ++itUpper); } template inline T& map::operator[](const Key& key) { iterator itLower(lower_bound(key)); // itLower->first is >= key. if((itLower == end()) || compare(key, (*itLower).first)) { itLower = base_type::DoInsertKey(true_type(), itLower, key); } return (*itLower).second; // Reference implementation of this function, which may not be as fast: //iterator it(base_type::insert(eastl::pair(key, T())).first); //return it->second; } template inline T& map::operator[](Key&& key) { iterator itLower(lower_bound(key)); // itLower->first is >= key. if((itLower == end()) || compare(key, (*itLower).first)) { itLower = base_type::DoInsertKey(true_type(), itLower, eastl::move(key)); } return (*itLower).second; // Reference implementation of this function, which may not be as fast: //iterator it(base_type::insert(eastl::pair(key, T())).first); //return it->second; } template inline T& map::at(const Key& key) { iterator itLower(lower_bound(key)); // itLower->first is >= key. if(itLower == end()) { #if EASTL_EXCEPTIONS_ENABLED throw std::out_of_range("map::at key does not exist"); #else EASTL_FAIL_MSG("map::at key does not exist"); #endif } return (*itLower).second; } template inline const T& map::at(const Key& key) const { const_iterator itLower(lower_bound(key)); // itLower->first is >= key. if(itLower == end()) { #if EASTL_EXCEPTIONS_ENABLED throw std::out_of_range("map::at key does not exist"); #else EASTL_FAIL_MSG("map::at key does not exist"); #endif } return (*itLower).second; } /////////////////////////////////////////////////////////////////////// // erase_if // // https://en.cppreference.com/w/cpp/container/map/erase_if /////////////////////////////////////////////////////////////////////// template void erase_if(map& c, Predicate predicate) { for (auto i = c.begin(), last = c.end(); i != last;) { if (predicate(*i)) { i = c.erase(i); } else { ++i; } } } /////////////////////////////////////////////////////////////////////// // multimap /////////////////////////////////////////////////////////////////////// template inline multimap::multimap(const allocator_type& allocator) : base_type(allocator) { } template inline multimap::multimap(const Compare& compare, const allocator_type& allocator) : base_type(compare, allocator) { } template inline multimap::multimap(const this_type& x) : base_type(x) { } template inline multimap::multimap(this_type&& x) : base_type(eastl::move(x)) { } template inline multimap::multimap(this_type&& x, const allocator_type& allocator) : base_type(eastl::move(x), allocator) { } template inline multimap::multimap(std::initializer_list ilist, const Compare& compare, const allocator_type& allocator) : base_type(ilist.begin(), ilist.end(), compare, allocator) { } template template inline multimap::multimap(Iterator itBegin, Iterator itEnd) : base_type(itBegin, itEnd, Compare(), EASTL_MULTIMAP_DEFAULT_ALLOCATOR) { } template inline typename multimap::insert_return_type multimap::insert(const Key& key) { return base_type::DoInsertKey(false_type(), key); } template inline typename multimap::value_compare multimap::value_comp() const { return value_compare(get_compare()); } template inline typename multimap::size_type multimap::erase(const Key& key) { const eastl::pair range(equal_range(key)); const size_type n = (size_type)eastl::distance(range.first, range.second); base_type::erase(range.first, range.second); return n; } template inline typename multimap::size_type multimap::count(const Key& key) const { const eastl::pair range(equal_range(key)); return (size_type)eastl::distance(range.first, range.second); } template inline eastl::pair::iterator, typename multimap::iterator> multimap::equal_range(const Key& key) { // There are multiple ways to implement equal_range. The implementation mentioned // in the C++ standard and which is used by most (all?) commercial STL implementations // is this: // return eastl::pair(lower_bound(key), upper_bound(key)); // // This does two tree searches -- one for the lower bound and one for the // upper bound. This works well for the case whereby you have a large container // and there are lots of duplicated values. We provide an alternative version // of equal_range called equal_range_small for cases where the user is confident // that the number of duplicated items is only a few. return eastl::pair(lower_bound(key), upper_bound(key)); } template inline eastl::pair::const_iterator, typename multimap::const_iterator> multimap::equal_range(const Key& key) const { // See comments above in the non-const version of equal_range. return eastl::pair(lower_bound(key), upper_bound(key)); } template inline eastl::pair::iterator, typename multimap::iterator> multimap::equal_range_small(const Key& key) { // We provide alternative version of equal_range here which works faster // for the case where there are at most small number of potential duplicated keys. const iterator itLower(lower_bound(key)); iterator itUpper(itLower); while((itUpper != end()) && !compare(key, itUpper.mpNode->mValue.first)) ++itUpper; return eastl::pair(itLower, itUpper); } template inline eastl::pair::const_iterator, typename multimap::const_iterator> multimap::equal_range_small(const Key& key) const { // We provide alternative version of equal_range here which works faster // for the case where there are at most small number of potential duplicated keys. const const_iterator itLower(lower_bound(key)); const_iterator itUpper(itLower); while((itUpper != end()) && !compare(key, itUpper.mpNode->mValue.first)) ++itUpper; return eastl::pair(itLower, itUpper); } /////////////////////////////////////////////////////////////////////// // erase_if // // https://en.cppreference.com/w/cpp/container/multimap/erase_if /////////////////////////////////////////////////////////////////////// template void erase_if(multimap& c, Predicate predicate) { // Erases all elements that satisfy the predicate pred from the container. for (auto i = c.begin(), last = c.end(); i != last;) { if (predicate(*i)) { i = c.erase(i); } else { ++i; } } } } // namespace eastl #endif // Header include guard