/* -*-c++-*- */ /* osgEarth - Geospatial SDK for OpenSceneGraph * Copyright 2020 Pelican Mapping * http://osgearth.org * * osgEarth is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace osgEarth { namespace Util { /** * A std::map-like map that uses a vector. * This benchmarks much faster than std::map or std::unordered_map for small sets. */ template> struct vector_map { struct ENTRY { KEY first; DATA second; }; using value_type = DATA; using container_t = std::vector; using iterator = typename container_t::iterator; using const_iterator = typename container_t::const_iterator; container_t _container; inline bool keys_equal(const KEY& a, const KEY& b) const { return LESS()(a, b) == false && LESS()(b, a) == false; } inline DATA& operator[](const KEY& key) { for(unsigned i=0; i<_container.size(); ++i) { if (keys_equal(_container[i].first, key)) { return _container[i].second; } } _container.resize(_container.size()+1); _container.back().first = key; return _container.back().second; } inline const_iterator begin() const { return _container.begin(); } inline const_iterator end() const { return _container.end(); } inline iterator begin() { return _container.begin(); } inline iterator end() { return _container.end(); } inline iterator find(const KEY& key) { for (unsigned i = 0; i < _container.size(); ++i) { if (keys_equal(_container[i].first, key)) { return _container.begin() + i; } } return _container.end(); } inline const_iterator find(const KEY& key) const { for(unsigned i=0; i<_container.size(); ++i) { if (keys_equal(_container[i].first, key)) { return _container.begin()+i; } } return _container.end(); } inline bool empty() const { return _container.empty(); } inline void clear() { _container.clear(); } inline void erase(const KEY& key) { for(unsigned i=0; i<_container.size(); ++i) { if (keys_equal(_container[i].first, key)) { if (i+1 < _container.size()) { _container[i] = _container[_container.size()-1]; } _container.resize(_container.size()-1); break; } } } inline int indexOf(const KEY& key) const { for(unsigned i=0; i<_container.size(); ++i) { if (keys_equal(_container[i].first, key)) { return i; } } return -1; } inline int size() const { return _container.size(); } template void insert(InputIterator a, InputIterator b) { for(InputIterator i = a; i != b; ++i) (*this)[i->first] = i->second; } }; template struct vector_set { typedef std::vector container_t; typedef typename container_t::iterator iterator; typedef typename container_t::const_iterator const_iterator; typedef typename container_t::size_type size_type; container_t _container; inline const_iterator begin() const { return _container.begin(); } inline const_iterator end() const { return _container.end(); } inline iterator begin() { return _container.begin(); } inline iterator end() { return _container.end(); } inline std::pair::const_iterator, bool> insert(const DATA& data) { const_iterator i = find(data); if (i != end()) { return std::make_pair(i, false); } else { _container.push_back(data); return std::make_pair(_container.end()-1, true); } } inline const_iterator find(const DATA& data) const { for (unsigned i = 0; i < _container.size(); ++i) { if (_container[i] == data) { return _container.begin() + i; } } return _container.end(); } inline bool contains(const DATA& data) const { return find(data) != _container.end(); } inline bool empty() const { return _container.empty(); } inline void clear() { _container.clear(); } inline size_type erase(const DATA& data) { for (unsigned i = 0; i < _container.size(); ++i) { if (_container[i] == data) { if (i + 1 < _container.size()) { _container[i] = _container[_container.size() - 1]; } _container.resize(_container.size() - 1); return 1; } } return 0; } inline int size() const { return _container.size(); } template void insert(InputIterator a, InputIterator b) { for (InputIterator i = a; i != b; ++i) insert(*i); } }; //------------------------------------------------------------------------ struct CacheStats { public: CacheStats( unsigned entries, unsigned maxEntries, unsigned queries, float hitRatio ) : _entries(entries), _maxEntries(maxEntries), _queries(queries), _hitRatio(hitRatio) { } /** dtor */ virtual ~CacheStats() { } unsigned _entries; unsigned _maxEntries; unsigned _queries; float _hitRatio; }; //------------------------------------------------------------------------ /** * Least-recently-used cache class. * K = key type, T = value type * * usage: * LRUCache cache; * cache.insert( key, value ); * LRUCache::Record rec; * if ( cache.get( key, rec ) ) * const T& value = rec.value(); */ template > class LRUCache { public: struct Record { Record() : _valid(false) { } Record(const T& value) : _value(value), _valid(true) { } bool valid() const { return _valid; } const T& value() const { return _value; } private: bool _valid; T _value; friend class LRUCache; }; using Functor = std::function; protected: typedef typename std::list::iterator lru_iter; typedef typename std::list lru_type; typedef typename std::pair map_value_type; typedef typename std::unordered_map map_type; typedef typename map_type::iterator map_iter; typedef typename map_type::const_iterator map_const_iter; map_type _map; lru_type _lru; unsigned _max; unsigned _buf; unsigned _queries; unsigned _hits; bool _threadsafe; mutable std::mutex _mutex; public: LRUCache( unsigned max =100 ) : _max(max), _threadsafe(false) { _queries = 0; _hits = 0; setMaxSize_impl(max); } LRUCache( bool threadsafe, unsigned max =100 ) : _max(max), _threadsafe(threadsafe) { _queries = 0; _hits = 0; setMaxSize_impl(max); } /** dtor */ virtual ~LRUCache() { } void insert( const K& key, const T& value ) { if ( _threadsafe ) { std::lock_guard lock(_mutex); insert_impl( key, value ); } else { insert_impl( key, value ); } } bool get( const K& key, Record& out ) { if ( _threadsafe ) { std::lock_guard lock(_mutex); get_impl( key, out ); } else { get_impl( key, out ); } return out.valid(); } bool has( const K& key ) { if ( _threadsafe ) { std::lock_guard lock(_mutex); return has_impl( key ); } else { return has_impl( key ); } } void erase( const K& key ) { if ( _threadsafe ) { std::lock_guard lock(_mutex); erase_impl( key ); } else { erase_impl( key ); } } void clear() { if ( _threadsafe ) { std::lock_guard lock(_mutex); clear_impl(); } else { clear_impl(); } } void setMaxSize( unsigned max ) { if ( _threadsafe ) { std::lock_guard lock(_mutex); setMaxSize_impl( max ); } else { setMaxSize_impl( max ); } } unsigned getMaxSize() const { return _max; } CacheStats getStats() const { return CacheStats( _map.size(), _max, _queries, _queries > 0 ? (float)_hits/(float)_queries : 0.0f ); } void forEach(const Functor& functor) const { if (_threadsafe) { std::lock_guard lock(_mutex); iterate_impl(functor); } else { iterate_impl(functor); } } private: void insert_impl( const K& key, const T& value ) { map_iter mi = _map.find( key ); if ( mi != _map.end() ) { _lru.erase( mi->second.second ); mi->second.first = value; _lru.push_back( key ); mi->second.second = _lru.end(); mi->second.second--; } else { _lru.push_back( key ); lru_iter last = _lru.end(); last--; _map[key] = std::make_pair(value, last); } if ( _map.size() > _max ) { for( unsigned i=0; i < _buf; ++i ) { const K& key = _lru.front(); _map.erase( key ); _lru.pop_front(); } } } void get_impl( const K& key, Record& result ) { _queries++; map_iter mi = _map.find( key ); if ( mi != _map.end() ) { _lru.erase( mi->second.second ); _lru.push_back( key ); lru_iter new_iter = _lru.end(); new_iter--; mi->second.second = new_iter; _hits++; result._value = mi->second.first; result._valid = true; } } bool has_impl( const K& key ) { return _map.find( key ) != _map.end(); } void erase_impl( const K& key ) { map_iter mi = _map.find( key ); if ( mi != _map.end() ) { _lru.erase( mi->second.second ); _map.erase( mi ); } } void clear_impl() { _lru.clear(); _map.clear(); _queries = 0; _hits = 0; } void setMaxSize_impl( unsigned max ) { _max = osg::maximum(max,10u); _buf = _max/10u; while( _map.size() > _max ) { const K& key = _lru.front(); _map.erase( key ); _lru.pop_front(); } } void iterate_impl(const Functor& func) const { for (auto i : _map) func(i.first, i.second.first); } }; //-------------------------------------------------------------------- /** * Same of osg::InlineVector, but with a superclass template parameter. */ template class InlineVector : public SuperClass { typedef typename std::vector vector_type; public: typedef typename vector_type::allocator_type allocator_type; typedef typename vector_type::value_type value_type; typedef typename vector_type::const_pointer const_pointer; typedef typename vector_type::pointer pointer; typedef typename vector_type::const_reference const_reference; typedef typename vector_type::reference reference; typedef typename vector_type::const_iterator const_iterator; typedef typename vector_type::iterator iterator; typedef typename vector_type::const_reverse_iterator const_reverse_iterator; typedef typename vector_type::reverse_iterator reverse_iterator; typedef typename vector_type::size_type size_type; typedef typename vector_type::difference_type difference_type; explicit InlineVector() : _impl() { } explicit InlineVector(size_type initial_size, const value_type& fill_value = value_type()) : _impl(initial_size, fill_value) { } template InlineVector(InputIterator first, InputIterator last) : _impl(first, last) { } InlineVector(const vector_type& other) : _impl(other) { } InlineVector(const InlineVector& other) : _impl(other._impl) { } InlineVector& operator=(const vector_type& other) { _impl = other; return *this; } InlineVector& operator=(const InlineVector& other) { _impl = other._impl; return *this; } virtual ~InlineVector() {} void clear() { _impl.clear(); } void resize(size_type new_size, const value_type& fill_value = value_type()) { _impl.resize(new_size, fill_value); } void reserve(size_type new_capacity) { _impl.reserve(new_capacity); } void swap(vector_type& other) { _impl.swap(other); } void swap(InlineVector& other) { _impl.swap(other._impl); } bool empty() const { return _impl.empty(); } size_type size() const { return _impl.size(); } size_type capacity() const { return _impl.capacity(); } size_type max_size() const { return _impl.max_size(); } allocator_type get_allocator() const { return _impl.get_allocator(); } const_iterator begin() const { return _impl.begin(); } iterator begin() { return _impl.begin(); } const_iterator end() const { return _impl.end(); } iterator end() { return _impl.end(); } const_reverse_iterator rbegin() const { return _impl.rbegin(); } reverse_iterator rbegin() { return _impl.rbegin(); } const_reverse_iterator rend() const { return _impl.rend(); } reverse_iterator rend() { return _impl.rend(); } const_reference operator[](size_type index) const { return _impl[index]; } reference operator[](size_type index) { return _impl[index]; } const_reference at(size_type index) const { return _impl.at(index); } reference at(size_type index) { return _impl.at(index); } void assign(size_type count, const value_type& value) { _impl.assign(count, value); } template void assign(Iter first, Iter last) { _impl.assign(first, last); } void push_back(const value_type& value) { _impl.push_back(value); } void pop_back() { _impl.pop_back(); } iterator erase(iterator where) { return _impl.erase(where); } iterator erase(iterator first, iterator last) { return _impl.erase(first, last); } iterator insert(iterator where, const value_type& value) { return _impl.insert(where, value); } template void insert(iterator where, InputIterator first, InputIterator last) { _impl.insert(where, first, last); } void insert(iterator where, size_type count, const value_type& value) { _impl.insert(where, count, value); } const_reference back() const { return _impl.back(); } reference back() { return _impl.back(); } const_reference front() const { return _impl.front(); } reference front() { return _impl.front(); } vector_type& asVector() { return _impl; } const vector_type& asVector() const { return _impl; } friend inline bool operator==(const InlineVector& left, const InlineVector& right) { return left._impl == right._impl; } friend inline bool operator==(const InlineVector& left, const std::vector& right) { return left._impl == right; } friend inline bool operator==(const std::vector& left, const InlineVector& right) { return left == right._impl; } friend inline bool operator!=(const InlineVector& left, const InlineVector& right) { return left._impl != right._impl; } friend inline bool operator!=(const InlineVector& left, const std::vector& right) { return left._impl != right; } friend inline bool operator!=(const std::vector& left, const InlineVector& right) { return left != right._impl; } friend inline bool operator<(const InlineVector& left, const InlineVector& right) { return left._impl < right._impl; } friend inline bool operator<(const InlineVector& left, const std::vector& right) { return left._impl < right; } friend inline bool operator<(const std::vector& left, const InlineVector& right) { return left < right._impl; } friend inline bool operator>(const InlineVector& left, const InlineVector& right) { return left._impl > right._impl; } friend inline bool operator>(const InlineVector& left, const std::vector& right) { return left._impl > right; } friend inline bool operator>(const std::vector& left, const InlineVector& right) { return left > right._impl; } friend inline bool operator<=(const InlineVector& left, const InlineVector& right) { return left._impl <= right._impl; } friend inline bool operator<=(const InlineVector& left, const std::vector& right) { return left._impl <= right; } friend inline bool operator<=(const std::vector& left, const InlineVector& right) { return left <= right._impl; } friend inline bool operator>=(const InlineVector& left, const InlineVector& right) { return left._impl >= right._impl; } friend inline bool operator>=(const InlineVector& left, const std::vector& right) { return left._impl >= right; } friend inline bool operator>=(const std::vector& left, const InlineVector& right) { return left >= right._impl; } private: vector_type _impl; }; /** Template for per-thread data storage */ template struct PerThread : public std::mutex { PerThread() { } T& get() { std::lock_guard lock(*this); return _data[std::this_thread::get_id()]; } void clear() { std::lock_guard lock(*this); _data.clear(); } typedef typename std::unordered_map container_t; typedef typename container_t::iterator iterator; // NB. lock before using these! iterator begin() { return _data.begin(); } iterator end() { return _data.end(); } private: container_t _data; }; /** Template for thread safe per-object data storage */ template struct PerObjectMap { PerObjectMap() { } bool threadsafe = true; DATA& get(KEY k) { { osgEarth::Threading::ScopedReadLock readLock(_mutex); typename std::unordered_map::iterator i = _data.find(k); if ( i != _data.end() ) return i->second; } { osgEarth::Threading::ScopedWriteLock lock(_mutex); typename std::unordered_map::iterator i = _data.find(k); if ( i != _data.end() ) return i->second; else return _data[k]; } } void remove(KEY k) { osgEarth::Threading::ScopedWriteLock exclusive(_mutex); _data.erase( k ); } private: std::unordered_map _data; osgEarth::Threading::ReadWriteMutex _mutex; }; /** Template for thread safe per-object data storage */ template struct PerObjectFastMap { PerObjectFastMap() { } bool threadsafe = true; struct Functor { virtual void operator()(DATA& data) =0; }; struct ConstFunctor { virtual void operator()(const DATA& data) const =0; }; DATA& get(KEY k) { osgEarth::Threading::scoped_lock_if lock(_mutex, threadsafe); typename std::unordered_map::iterator i = _data.find(k); if ( i != _data.end() ) return i->second; else return _data[k]; } void remove(KEY k) { osgEarth::Threading::scoped_lock_if lock(_mutex, threadsafe); _data.erase( k ); } void forEach(Functor& functor) { osgEarth::Threading::scoped_lock_if lock(_mutex, threadsafe); for (typename std::unordered_map::iterator i = _data.begin(); i != _data.end(); ++i) functor.operator()(i->second); } void forEach(std::function functor) { osgEarth::Threading::scoped_lock_if lock(_mutex, threadsafe); for (auto& entry : _data) functor(entry.second); } void forEach(ConstFunctor& functor) const { osgEarth::Threading::scoped_lock_if lock(_mutex, threadsafe); for (typename std::unordered_map::const_iterator i = _data.begin(); i != _data.end(); ++i) functor.operator()(i->second); } void forEach(std::function functor) const { osgEarth::Threading::scoped_lock_if lock(_mutex, threadsafe); for (auto& entry : _data) functor(entry.second); } unsigned size() const { osgEarth::Threading::scoped_lock_if lock(_mutex, threadsafe); return _data.size(); } void clear() { osgEarth::Threading::scoped_lock_if lock(_mutex, threadsafe); _data.clear(); } private: std::unordered_map _data; mutable std::mutex _mutex; }; /** Template for thread safe per-object data storage */ template struct PerObjectRefMap { PerObjectRefMap() { } DATA* get(KEY k) { osgEarth::Threading::ScopedReadLock lock(_mutex); typename std::unordered_map>::const_iterator i = _data.find(k); if ( i != _data.end() ) return i->second.get(); return 0L; } DATA* getOrCreate(KEY k, DATA* newDataIfNeeded) { osg::ref_ptr _refReleaser = newDataIfNeeded; { osgEarth::Threading::ScopedReadLock lock(_mutex); typename std::unordered_map>::const_iterator i = _data.find(k); if ( i != _data.end() ) return i->second.get(); } { osgEarth::Threading::ScopedWriteLock lock(_mutex); typename std::unordered_map>::iterator i = _data.find(k); if ( i != _data.end() ) return i->second.get(); _data[k] = newDataIfNeeded; return newDataIfNeeded; } } void remove(KEY k) { osgEarth::Threading::ScopedWriteLock exclusive(_mutex); _data.erase( k ); } void remove(DATA* data) { osgEarth::Threading::ScopedWriteLock exclusive(_mutex); for( typename std::unordered_map>::iterator i = _data.begin(); i != _data.end(); ++i ) { if ( i->second.get() == data ) { _data.erase( i ); break; } } } private: std::unordered_map> _data; osgEarth::Threading::ReadWriteMutex _mutex; }; // borrowed from osg::buffered_object. Auto-resizing array. template class AutoArray { public: using iterator = typename std::vector::iterator; using const_iterator = typename std::vector::const_iterator; inline AutoArray() { } inline AutoArray(unsigned int size) : _array(size) { } AutoArray& operator = (const AutoArray& rhs) { _array = rhs._array; return *this; } inline void setAllElementsTo(const T& t) { std::fill(_array.begin(), _array.end(), t); } inline void clear() { _array.clear(); } inline bool empty() const { return _array.empty(); } inline unsigned int size() const { return _array.size(); } inline void resize(unsigned int newSize) { _array.resize(newSize); } inline iterator begin() { return _array.begin(); } inline const_iterator begin() const { return _array.begin(); } inline iterator end() { return _array.end(); } inline const_iterator end() const { return _array.end(); } inline T& operator[] (unsigned int pos) { if (_array.size() <= pos) _array.resize(pos + 1); return _array[pos]; } inline const T& operator[] (unsigned int pos) const { // automatically resize array. if (_array.size() <= pos) _array.resize(pos + 1); return _array[pos]; } inline T& back() { return _array[size()-1]; } inline const T& back() const { return _array[size()-1]; } protected: mutable std::vector _array; }; // A generic osg::Referenced vector template class RefVector : public InlineVector { }; /** * Thread-safe queue. */ template class MutexedQueue { public: //! Push a new item safely void push(const T& t) { std::lock_guard lock(_m); _queue.push(t); } //! Remove the front item safely void pop() { std::lock_guard lock(_m); _queue.pop(); } //! Safely return a copy of the item at the head of //! the queue T front() { std::lock_guard lock(_m); return _queue.empty() ? _default : _queue.front(); } //! Safely remove the item at the head of the queue //! and return it T take_front() { std::lock_guard lock(_m); T t = _queue.empty() ? _default : _queue.front(); if (_queue.empty() == false) _queue.pop(); return t; } //! Safely clear the queue void clear() { std::lock_guard lock(_m); _queue.swap(std::queue()); } //! True if the queue is empty (but only at the //! instant of the call.) bool empty() const { return _queue.empty(); } private: std::queue _queue; mutable std::mutex _m; T _default; }; /** * A circular list that you can pull pointers from * NOT thread safe. */ template class OSGEARTH_EXPORT RoundRobin { public: using iterator = typename std::list::iterator; using const_iterator = typename std::list::const_iterator; void add(const T& t) { _list.push_back(t); if (_list.size() == 1) _iter = begin(); } T next() { OE_HARD_ASSERT(_list.size() > 0); T t = *_iter++; if (_iter == end()) _iter = begin(); return t; } iterator begin() { return _list.begin(); } const_iterator begin() const { return _list.begin(); } iterator end() { return _list.end(); } const_iterator end() const { return _list.end(); } bool empty() const { return _list.empty(); } private: std::list _list; iterator _iter; }; } }