/** * @file llsortedvector.h * @author Nat Goodspeed * @date 2012-04-08 * @brief LLSortedVector class wraps a vector that we maintain in sorted * order so we can perform binary-search lookups. * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ */ #if ! defined(LL_LLSORTEDVECTOR_H) #define LL_LLSORTEDVECTOR_H #include #include /** * LLSortedVector contains a std::vector that we keep sorted on the * first of the pair. This makes insertion somewhat more expensive than simple * std::vector::push_back(), but allows us to use binary search for lookups. * It's intended for small aggregates where lookup is far more performance- * critical than insertion; in such cases a binary search on a small, sorted * std::vector can be more performant than a std::map lookup. */ template class LLSortedVector { public: typedef LLSortedVector self; typedef KEY key_type; typedef VALUE mapped_type; typedef std::pair value_type; typedef std::vector PairVector; typedef typename PairVector::iterator iterator; typedef typename PairVector::const_iterator const_iterator; /// Empty LLSortedVector() {} /// Fixed initial size LLSortedVector(std::size_t size): mVector(size) {} /// Bulk load template LLSortedVector(ITER begin, ITER end): mVector(begin, end) { // Allow caller to dump in a bunch of (pairs convertible to) // value_type if desired, but make sure we sort afterwards. std::sort(mVector.begin(), mVector.end()); } /// insert(key, value) std::pair insert(const key_type& key, const mapped_type& value) { return insert(value_type(key, value)); } /// insert(value_type) std::pair insert(const value_type& pair) { typedef std::pair iterbool; iterator found = std::lower_bound(mVector.begin(), mVector.end(), pair, less()); // have to check for end() before it's even valid to dereference if (found == mVector.end()) { std::size_t index(mVector.size()); mVector.push_back(pair); // don't forget that push_back() invalidates 'found' return iterbool(mVector.begin() + index, true); } if (found->first == pair.first) { return iterbool(found, false); } // remember that insert() invalidates 'found' -- save index std::size_t index(found - mVector.begin()); mVector.insert(found, pair); // okay, convert from index back to iterator return iterbool(mVector.begin() + index, true); } iterator begin() { return mVector.begin(); } iterator end() { return mVector.end(); } const_iterator begin() const { return mVector.begin(); } const_iterator end() const { return mVector.end(); } bool empty() const { return mVector.empty(); } std::size_t size() const { return mVector.size(); } /// find iterator find(const key_type& key) { iterator found = std::lower_bound(mVector.begin(), mVector.end(), value_type(key, mapped_type()), less()); if (found == mVector.end() || found->first != key) return mVector.end(); return found; } const_iterator find(const key_type& key) const { return const_cast(this)->find(key); } private: // Define our own 'less' comparator so we can specialize without messing // with std::less. template struct less: public std::less {}; // Specialize 'less' for an LLSortedVector::value_type involving // std::type_info*. This is one of LLSortedVector's foremost use cases. We // specialize 'less' rather than just defining a specific comparator // because LLSortedVector should be usable for other key_types as well. template struct less< std::pair >: public std::binary_function, std::pair, bool> { bool operator()(const std::pair& lhs, const std::pair& rhs) const { return lhs.first->before(*rhs.first); } }; // Same as above, but with const std::type_info*. template struct less< std::pair >: public std::binary_function, std::pair, bool> { bool operator()(const std::pair& lhs, const std::pair& rhs) const { return lhs.first->before(*rhs.first); } }; PairVector mVector; }; #endif /* ! defined(LL_LLSORTEDVECTOR_H) */