From 4fd57501bbc914eb973a397b24be0d2dbe51d007 Mon Sep 17 00:00:00 2001 From: fonsinchen Date: Sun, 9 Jun 2013 12:52:11 +0000 Subject: [PATCH] (svn r25348) -Add: multimap implementation with guaranteed order between equal keys --- projects/openttd_vs100.vcxproj | 1 + projects/openttd_vs100.vcxproj.filters | 3 + projects/openttd_vs80.vcproj | 4 + projects/openttd_vs90.vcproj | 4 + source.list | 1 + src/core/multimap.hpp | 388 +++++++++++++++++++++++++ 6 files changed, 401 insertions(+) create mode 100644 src/core/multimap.hpp diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj index 41aeeabacc..5dc33f4905 100644 --- a/projects/openttd_vs100.vcxproj +++ b/projects/openttd_vs100.vcxproj @@ -650,6 +650,7 @@ + diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters index f2900c846f..70e447850f 100644 --- a/projects/openttd_vs100.vcxproj.filters +++ b/projects/openttd_vs100.vcxproj.filters @@ -1179,6 +1179,9 @@ Core Source Code + + Core Source Code + Core Source Code diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index 5c866b99dd..3d72082b15 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -1878,6 +1878,10 @@ RelativePath=".\..\src\core\mem_func.hpp" > + + diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj index 96120ed473..29ec12e2bd 100644 --- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -1875,6 +1875,10 @@ RelativePath=".\..\src\core\mem_func.hpp" > + + diff --git a/source.list b/source.list index be324f335f..2edab9e7bb 100644 --- a/source.list +++ b/source.list @@ -400,6 +400,7 @@ core/geometry_type.hpp core/math_func.cpp core/math_func.hpp core/mem_func.hpp +core/multimap.hpp core/overflowsafe_type.hpp core/pool_func.cpp core/pool_func.hpp diff --git a/src/core/multimap.hpp b/src/core/multimap.hpp new file mode 100644 index 0000000000..e906677141 --- /dev/null +++ b/src/core/multimap.hpp @@ -0,0 +1,388 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file multimap.hpp Multimap with deterministic ordering of items with equal keys. */ + +#ifndef MULTIMAP_HPP +#define MULTIMAP_HPP + +#include +#include + +template +class MultiMap; + +/** + * STL-style iterator for MultiMap. + * @tparam Tmap_iter Iterator type for the map in the MultiMap. + * @tparam Tlist_iter Iterator type for the lists in the MultiMap. + * @tparam Tkey Key type of the MultiMap. + * @tparam Tvalue Value type of the MultMap. + * @tparam Tcompare Comparator type for keys of the MultiMap. + */ +template +class MultiMapIterator { +protected: + friend class MultiMap; + typedef MultiMapIterator Self; + + Tlist_iter list_iter; ///< Iterator pointing to current position in the current list of items with equal keys. + Tmap_iter map_iter; ///< Iterator pointing to the position of the current list of items with equal keys in the map. + + /** + * Flag to show that the iterator has just "walked" a step in the map. + * We cannot check the current list for that as we might have reached end() of the map. In that case we'd need to + * set list_iter to some sort of "invalid" state, but that's impossible as operator== yields undefined behaviour + * if the iterators don't belong to the same list and there is no list at end(). So if we created a static empty + * list and an "invalid" iterator in that we could not determine if the iterator is invalid while it's valid. We + * can also not determine if the map iterator is valid while we don't have the map; so in the end it's easiest to + * just introduce an extra flag. + */ + bool list_valid; + +public: + /** + * Simple, dangerous constructor to allow later assignment with operator=. + */ + MultiMapIterator() : list_valid(false) {} + + /** + * Constructor to allow possibly const iterators to be assigned from possibly + * non-const map iterators. You can assign end() like this. + * @tparam Tnon_const Iterator type assignable to Tmap_iter (which might be const). + * @param mi One such iterator. + */ + template + MultiMapIterator(Tnon_const mi) : map_iter(mi), list_valid(false) {} + + /** + * Constructor to allow specifying an exact position in map and list. You cannot + * construct end() like this as the constructor will actually check li and mi->second + * for list_valid. + * @param mi Iterator in the map. + * @param li Iterator in the list. + */ + MultiMapIterator(Tmap_iter mi, Tlist_iter li) : list_iter(li), map_iter(mi) + { + this->list_valid = (this->list_iter != this->map_iter->second.begin()); + } + + /** + * Assignment iterator like constructor with the same signature. + * @tparam Tnon_const Iterator type assignable to Tmap_iter (which might be const). + * @param mi One such iterator. + * @return This iterator. + */ + template + Self &operator=(Tnon_const mi) + { + this->map_iter = mi; + this->list_valid = false; + return *this; + } + + /** + * Dereference operator. Works just like usual STL operator*() on various containers. + * Doesn't do a lot of checks for sanity, just like STL. + * @return The value associated with the item this iterator points to. + */ + Tvalue &operator*() const + { + assert(!this->map_iter->second.empty()); + return this->list_valid ? + this->list_iter.operator*() : + this->map_iter->second.begin().operator*(); + } + + /** + * Same as operator*(), but returns a pointer. + * @return Pointer to the value this iterator points to. + */ + Tvalue *operator->() const + { + assert(!this->map_iter->second.empty()); + return this->list_valid ? + this->list_iter.operator->() : + this->map_iter->second.begin().operator->(); + } + + inline const Tmap_iter &GetMapIter() const { return this->map_iter; } + inline const Tlist_iter &GetListIter() const { return this->list_iter; } + inline bool ListValid() const { return this->list_valid; } + + const Tkey &GetKey() const { return this->map_iter->first; } + + /** + * Prefix increment operator. Increment the iterator and set it to the + * next item in the MultiMap. This either increments the list iterator + * or the map iterator and sets list_valid accordingly. + * @return This iterator after incrementing. + */ + Self &operator++() + { + assert(!this->map_iter->second.empty()); + if (this->list_valid) { + if (++this->list_iter == this->map_iter->second.end()) { + ++this->map_iter; + this->list_valid = false; + } + } else { + this->list_iter = ++(this->map_iter->second.begin()); + if (this->list_iter == this->map_iter->second.end()) { + ++this->map_iter; + } else { + this->list_valid = true; + } + } + return *this; + } + + /** + * Postfix increment operator. Same as prefix increment, but return the + * previous state. + * @param dummy param to mark postfix. + * @return This iterator before incrementing. + */ + Self operator++(int) + { + Self tmp = *this; + this->operator++(); + return tmp; + } + + /** + * Prefix decrement operator. Decrement the iterator and set it to the + * previous item in the MultiMap. + * @return This iterator after decrementing. + */ + Self &operator--() + { + assert(!this->map_iter->second.empty()); + if (!this->list_valid) { + --this->map_iter; + this->list_iter = this->map_iter->second.end(); + assert(!this->map_iter->second.empty()); + } + + this->list_valid = (--this->list_iter != this->map_iter->second.begin()); + return *this; + } + + /** + * Postfix decrement operator. Same as prefix decrement, but return the + * previous state. + * @param dummy param to mark postfix. + * @return This iterator before decrementing. + */ + Self operator--(int) + { + Self tmp = *this; + this->operator--(); + return tmp; + } +}; + +/* Generic comparison functions for const/non-const MultiMap iterators and map iterators */ + +/** + * Compare two MultiMap iterators. Iterators are equal if + * 1. Their map iterators are equal. + * 2. They agree about list_valid. + * 3. If list_valid they agree about list_iter. + * Lots of template parameters to make all possible const and non-const types of MultiMap iterators + * (on maps with const and non-const values) comparable to each other. + * @param iter1 First iterator to compare. + * @param iter2 Second iterator to compare. + * @return If iter1 and iter2 are equal. + */ +template +bool operator==(const MultiMapIterator &iter1, const MultiMapIterator &iter2) +{ + if (iter1.GetMapIter() != iter2.GetMapIter()) return false; + if (!iter1.ListValid()) return !iter2.ListValid(); + return iter2.ListValid() ? + iter1.GetListIter() == iter2.GetListIter() : false; +} + +/** + * Inverse of operator==(). + * Lots of template parameters to make all possible const and non-const types of MultiMap iterators + * (on maps with const and non-const values) comparable to each other. + * @param iter1 First iterator to compare. + * @param iter2 Second iterator to compare. + * @return If iter1 and iter2 are not equal. + */ +template +bool operator!=(const MultiMapIterator &iter1, const MultiMapIterator &iter2) +{ + return !(iter1 == iter2); +} + +/** + * Check if a MultiMap iterator is at the begin of a list pointed to by the given map iterator. + * Lots of template parameters to make all possible const and non-const types of MultiMap iterators + * (on maps with const and non-const values) comparable to all possible types of map iterators. + * @param iter1 MultiMap iterator. + * @param iter2 Map iterator. + * @return If iter1 points to the begin of the list pointed to by iter2. + */ +template +bool operator==(const MultiMapIterator &iter1, const Tmap_iter2 &iter2) +{ + return !iter1.ListValid() && iter1.GetMapIter() == iter2; +} + +/** + * Inverse of operator==() with same signature. + * @param iter1 MultiMap iterator. + * @param iter2 Map iterator. + * @return If iter1 doesn't point to the begin of the list pointed to by iter2. + */ +template +bool operator!=(const MultiMapIterator &iter1, const Tmap_iter2 &iter2) +{ + return iter1.ListValid() || iter1.GetMapIter() != iter2; +} + +/** + * Same as operator==() with reversed order of arguments. + * @param iter2 Map iterator. + * @param iter1 MultiMap iterator. + * @return If iter1 points to the begin of the list pointed to by iter2. + */ +template +bool operator==(const Tmap_iter2 &iter2, const MultiMapIterator &iter1) +{ + return !iter1.ListValid() && iter1.GetMapIter() == iter2; +} + +/** + * Same as operator!=() with reversed order of arguments. + * @param iter2 Map iterator. + * @param iter1 MultiMap iterator. + * @return If iter1 doesn't point to the begin of the list pointed to by iter2. + */ +template +bool operator!=(const Tmap_iter2 &iter2, const MultiMapIterator &iter1) +{ + return iter1.ListValid() || iter1.GetMapIter() != iter2; +} + + +/** + * Hand-rolled multimap as map of lists. Behaves mostly like a list, but is sorted + * by Tkey so that you can easily look up ranges of equal keys. Those ranges are + * internally ordered in a deterministic way (contrary to STL multimap). All + * STL-compatible members are named in STL style, all others are named in OpenTTD + * style. + */ +template > +class MultiMap : public std::map, Tcompare > { +public: + typedef typename std::list List; + typedef typename List::iterator ListIterator; + typedef typename List::const_iterator ConstListIterator; + + typedef typename std::map Map; + typedef typename Map::iterator MapIterator; + typedef typename Map::const_iterator ConstMapIterator; + + typedef MultiMapIterator iterator; + typedef MultiMapIterator const_iterator; + + /** + * Erase the value pointed to by an iterator. The iterator may be invalid afterwards. + * @param it Iterator pointing at some value. + * @return Iterator to the element after the deleted one (or invalid). + */ + iterator erase(iterator it) + { + List &list = it.map_iter->second; + assert(!list.empty()); + if (it.list_valid) { + it.list_iter = list.erase(it.list_iter); + /* This can't be the first list element as otherwise list_valid would have + * to be false. So the list cannot be empty here. */ + if (it.list_iter == list.end()) { + ++it.map_iter; + it.list_valid = false; + } + } else { + list.erase(list.begin()); + if (list.empty()) this->Map::erase(it.map_iter++); + } + return it; + } + + /** + * Insert a value at the end of the range with the specified key. + * @param key Key to be inserted at. + * @param val Value to be inserted. + */ + void Insert(const Tkey &key, const Tvalue &val) + { + List &list = (*this)[key]; + list.push_back(val); + assert(!list.empty()); + } + + /** + * Count all items in this MultiMap. This involves iterating over the map. + * @return Number of items in the MultiMap. + */ + size_t size() const + { + size_t ret = 0; + for (ConstMapIterator it = this->Map::begin(); it != this->Map::end(); ++it) { + ret += it->second.size(); + } + return ret; + } + + /** + * Count the number of ranges with equal keys in this MultiMap. + * @return Number of ranges with equal keys. + */ + size_t MapSize() const + { + return this->Map::size(); + } + + /** + * Get a pair of iterators specifying a range of items with equal keys. + * @param key Key to look for. + * @return Range of items with given key. + */ + std::pair equal_range(const Tkey &key) + { + MapIterator begin(this->lower_bound(key)); + if (begin != this->Map::end() && begin->first == key) { + MapIterator end = begin; + return std::make_pair(begin, ++end); + } + return std::make_pair(begin, begin); + } + + /** + * Get a pair of constant iterators specifying a range of items with equal keys. + * @param key Key to look for. + * @return Constant range of items with given key. + */ + std::pair equal_range(const Tkey &key) const + { + ConstMapIterator begin(this->lower_bound(key)); + if (begin != this->Map::end() && begin->first == key) { + ConstMapIterator end = begin; + return std::make_pair(begin, ++end); + } + return std::make_pair(begin, begin); + } +}; + +#endif /* MULTIMAP_HPP */