// ********************************************************************** // // Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** #ifndef FREEZE_MAP_H #define FREEZE_MAP_H #include #include #include #include #include // // Berkeley DB's DbEnv // class DbEnv; namespace Freeze { class IteratorHelper; class MapHelper; class MapIndexI; class MapHelperI; class IteratorHelperI; class SharedDb; class FREEZE_API KeyCompareBase : public IceUtil::Shared { public: KeyCompareBase(bool); bool compareEnabled() const; virtual int compare(const Key&, const Key&) = 0; private: const bool _enabled; }; typedef IceUtil::Handle KeyCompareBasePtr; class FREEZE_API MapIndexBase : public KeyCompareBase { public: virtual ~MapIndexBase(); const std::string& name() const; IteratorHelper* begin(bool) const; IteratorHelper* untypedFind(const Key&, bool, bool) const; IteratorHelper* untypedLowerBound(const Key&, bool) const; IteratorHelper* untypedUpperBound(const Key&, bool) const; int untypedCount(const Key&) const; // // Implemented by the generated code // virtual void marshalKey(const Value&, Key&) const = 0; protected: MapIndexBase(const std::string&, bool); Ice::CommunicatorPtr _communicator; private: friend class MapHelperI; friend class IteratorHelperI; friend class SharedDb; friend class MapDb; std::string _name; MapIndexI* _impl; const MapHelperI* _map; }; typedef IceUtil::Handle MapIndexBasePtr; class FREEZE_API MapHelper { public: static MapHelper* create(const ConnectionPtr& connection, const std::string& dbName, const std::string& key, const std::string& value, const KeyCompareBasePtr&, const std::vector&, bool createDb); static void recreate(const ConnectionPtr& connection, const std::string& dbName, const std::string& key, const std::string& value, const KeyCompareBasePtr&, const std::vector&); virtual ~MapHelper() = 0; virtual IteratorHelper* find(const Key&, bool) const = 0; virtual IteratorHelper* lowerBound(const Key&, bool) const = 0; virtual IteratorHelper* upperBound(const Key&, bool) const = 0; virtual void put(const Key&, const Value&) = 0; virtual size_t erase(const Key&) = 0; virtual size_t count(const Key&) const = 0; virtual void clear() = 0; virtual void destroy() = 0; virtual size_t size() const = 0; virtual void closeAllIterators() = 0; virtual const MapIndexBasePtr& index(const std::string&) const = 0; virtual void closeDb() = 0; virtual ConnectionPtr getConnection() const = 0; }; class FREEZE_API IteratorHelper { public: static IteratorHelper* create(const MapHelper& m, bool readOnly); virtual ~IteratorHelper() = 0; virtual IteratorHelper* clone() const = 0; virtual const Key* get() const = 0; virtual void get(const Key*&, const Value*&) const = 0; virtual void set(const Value&) = 0; virtual void erase() = 0; virtual bool next() const = 0; }; // // Forward declaration // template class Map; template class ConstIterator; // // This is necessary for MSVC support. // struct IteratorBase { typedef std::forward_iterator_tag iterator_category; }; // // Database iterator. This implements a forward iterator with the // restriction that it's only possible to explicitely write back into // the database. // // Two iterators are equal if they use the same database and their // current records have the same key. // // TODO: It's possible to implement bidirectional iterators, if // necessary. // template class Iterator : public IteratorBase { public: typedef ptrdiff_t difference_type; typedef std::pair value_type; typedef value_type* pointer; typedef value_type& reference; Iterator(MapHelper& mapHelper, const Ice::CommunicatorPtr& communicator) : _helper(IteratorHelper::create(mapHelper, false)), _communicator(communicator), _refValid(false) { } Iterator(IteratorHelper* helper, const Ice::CommunicatorPtr& communicator) : _helper(helper), _communicator(communicator), _refValid(false) { } Iterator() : _refValid(false) { } Iterator(const Iterator& rhs) : _communicator(rhs._communicator), _refValid(false) { if(rhs._helper.get() != 0) { _helper.reset(rhs._helper->clone()); } } Iterator& operator=(const Iterator& rhs) { if(this != &rhs) { if(rhs._helper.get() != 0) { _helper.reset(rhs._helper->clone()); } else { _helper.reset(); } _communicator = rhs._communicator; _refValid = false; } return *this; } ~Iterator() { } bool operator==(const Iterator& rhs) const { if(_helper.get() == rhs._helper.get()) { return true; } if(_helper.get() != 0 && rhs._helper.get() != 0) { const Key* lhsKey = _helper->get(); const Key* rhsKey = rhs._helper->get(); if(lhsKey != 0 && rhsKey != 0) { return *lhsKey == *rhsKey; } } return false; } bool operator!=(const Iterator& rhs) const { return !(*this == rhs); } Iterator& operator++() { incr(); return *this; } Iterator operator++(int) { Iterator tmp = *this; incr(); return tmp; } // // Note that this doesn't follow the regular iterator mapping: // // value_type& operator*(), value_type operator*() const // value_type& operator*() const { if(!_refValid) { key_type key; mapped_type value; getCurrentValue(key, value); // // !IMPORTANT! // // This method has to cache the returned value to implement // operator->(). // const_cast(_ref.first) = key; const_cast(_ref.second) = value; _refValid = true; } return _ref; } value_type* operator->() { return &(operator*()); } // // This special method allows writing back into the database. // void set(const mapped_type& value) { assert(_helper.get()); Value v; ValueCodec::write(value, v, _communicator); _helper->set(v); _refValid = false; } private: void incr() { assert(_helper.get() != 0); if(!_helper->next()) { // // The iterator has been moved past the end, and is now // invalid. // _helper.reset(); } _refValid = false; } void getCurrentValue(key_type& key, mapped_type& value) const { assert(_helper.get() != 0); const Key* k = 0; const Value* v = 0; _helper->get(k, v); assert(k != 0); assert(v != 0); KeyCodec::read(key, *k, _communicator); ValueCodec::read(value, *v, _communicator); } friend class ConstIterator; friend class Map; std::auto_ptr _helper; Ice::CommunicatorPtr _communicator; // // Cached last return value. This is so that operator->() can // actually return a pointer. The cached value is reused across // multiple calls to operator->() if _refValid is true, which // avoids problems in certain situations. For example, if // _ref.second is an STL container and you use an STL algorithm // such as transform, STLport (debug build) asserts that the // addresses of the containers are the same. This would fail if // the same value was not returned on subsequent calls to // operator->(). // mutable value_type _ref; mutable bool _refValid; }; // // See Iterator comments for design notes // template class ConstIterator : public IteratorBase { public: typedef ptrdiff_t difference_type; typedef std::pair value_type; typedef value_type* pointer; typedef value_type& reference; ConstIterator(MapHelper& mapHelper, const Ice::CommunicatorPtr& communicator) : _helper(IteratorHelper::create(mapHelper, true)), _communicator(_communicator), _refValid(false) { } ConstIterator(IteratorHelper* helper, const Ice::CommunicatorPtr& communicator) : _helper(helper), _communicator(communicator), _refValid(false) { } ConstIterator() : _refValid(false) { } ConstIterator(const ConstIterator& rhs) : _communicator(rhs._communicator), _refValid(false) { if(rhs._helper.get() != 0) { _helper.reset(rhs._helper->clone()); } } // // A Iterator can be converted to a ConstIterator (but not // vice versa) - same for operator=. // ConstIterator(const Iterator& rhs) : _refValid(false) { if(rhs._helper.get() != 0) { _helper.reset(rhs._helper->clone()); } _communicator = rhs._communicator; } ConstIterator& operator=(const ConstIterator& rhs) { if(this != &rhs) { if(rhs._helper.get() != 0) { _helper.reset(rhs._helper->clone()); } else { _helper.reset(); } _communicator = rhs._communicator; _refValid = false; } return *this; } // // Create const_iterator from iterator. // ConstIterator& operator=(const Iterator& rhs) { if(rhs._helper.get() != 0) { _helper.reset(rhs._helper->clone()); } else { _helper.reset(); } _communicator = rhs._communicator; _refValid = false; return *this; } ~ConstIterator() { } bool operator==(const ConstIterator& rhs) { if(_helper.get() == rhs._helper.get()) { return true; } if(_helper.get() != 0 && rhs._helper.get() != 0) { const Key* lhsKey = _helper->get(); const Key* rhsKey = rhs._helper->get(); if(lhsKey != 0 && rhsKey != 0) { return *lhsKey == *rhsKey; } } return false; } bool operator!=(const ConstIterator& rhs) { return !(*this == rhs); } ConstIterator& operator++() { incr(); return *this; } ConstIterator operator++(int) { ConstIterator tmp = *this; incr(); return tmp; } // // Note that this doesn't follow the regular iterator mapping: // // value_type operator*() const // value_type& operator*() const { if(!_refValid) { key_type key; mapped_type value; getCurrentValue(key, value); // // !IMPORTANT! // // This method has to cache the returned value to implement // operator->(). // const_cast(_ref.first) = key; const_cast(_ref.second) = value; _refValid = true; } return _ref; } pointer operator->() const { return &(operator*()); } private: void incr() { assert(_helper.get() != 0); if(!_helper->next()) { // // The iterator has been moved past the end, and is now // invalid. // _helper.reset(); } _refValid = false; } void getCurrentValue(key_type& key, mapped_type& value) const { assert(_helper.get() != 0); const Key* k = 0; const Value* v = 0; _helper->get(k, v); assert(k != 0); assert(v != 0); KeyCodec::read(key, *k, _communicator); ValueCodec::read(value, *v, _communicator); } friend class Map; std::auto_ptr _helper; Ice::CommunicatorPtr _communicator; // // Cached last return value. This is so that operator->() can // actually return a pointer. The cached value is reused across // multiple calls to operator->() if _refValid is true, which // avoids problems in certain situations. For example, if // _ref.second is an STL container and you use an STL algorithm // such as transform, STLport (debug build) asserts that the // addresses of the containers are the same. This would fail // if the same value was not returned on subsequent calls // to operator->(). // mutable value_type _ref; mutable bool _refValid; }; #if defined(_MSC_VER) && (_MSC_VER < 1300) // // Without partial template specialization // struct IceEncodingCompare { bool operator()(...) { return false; } }; template inline bool enableKeyCompare(const Compare&) { return true; } template<> inline bool enableKeyCompare(const IceEncodingCompare&) { return false; } # else struct IceEncodingCompare {}; #endif template class KeyCompare : public KeyCompareBase { public: KeyCompare(const Compare& compare, const Ice::CommunicatorPtr& communicator) : #if defined(_MSC_VER) && (_MSC_VER < 1300) KeyCompareBase(enableKeyCompare(compare)), #else KeyCompareBase(true), #endif _compare(compare), _communicator(communicator) {} virtual int compare(const Key& dbKey1, const Key& dbKey2) { key_type key1; KeyCodec::read(key1, dbKey1, _communicator); key_type key2; KeyCodec::read(key2, dbKey2, _communicator); if(_compare(key1, key2)) { return -1; } else if(_compare(key2, key1)) { return 1; } else { return 0; } } private: Compare _compare; Ice::CommunicatorPtr _communicator; }; #if !defined(_MSC_VER) || (_MSC_VER >= 1300) // // Partial template specialization: // do nothing for the IceEncodingCompare comparator // template class KeyCompare : public KeyCompareBase { public: KeyCompare(const IceEncodingCompare&, const Ice::CommunicatorPtr&): KeyCompareBase(false) {} virtual int compare(const Key&, const Key&) { assert(0); return 0; } }; #endif // // Need to separate MapIndex template class because _communicator is // set later // template class MapIndex : public MapIndexBase { public: virtual int compare(const Key& dbKey1, const Key& dbKey2) { key_type key1; KeyCodec::read(key1, dbKey1, _communicator); key_type key2; KeyCodec::read(key2, dbKey2, _communicator); if(_compare(key1, key2)) { return -1; } else if(_compare(key2, key1)) { return 1; } else { return 0; } } protected: MapIndex(const std::string& name, const Compare& compare) : #if defined(_MSC_VER) && (_MSC_VER < 1300) MapIndexBase(name, enableKeyCompare(compare)), #else MapIndexBase(name, true), #endif _compare(compare) {} private: Compare _compare; }; #if !defined(_MSC_VER) || (_MSC_VER >= 1300) // // Partial template specialization: // do nothing for the IceEncodingCompare comparator // template class MapIndex : public MapIndexBase { public: virtual int compare(const Key&, const Key&) { assert(0); return 0; } protected: MapIndex(const std::string& name, const IceEncodingCompare&): MapIndexBase(name, false) {} }; #endif // // A sorted map, similar to a std::map, with one notable difference: // operator[] is not provided. // // // TODO: implement bidirectional iterators. // template class Map { public: typedef std::pair value_type; typedef Iterator iterator; typedef ConstIterator const_iterator; // // No definition for reference, const_reference, pointer or // const_pointer. // typedef size_t size_type; typedef ptrdiff_t difference_type; // // Allocators are not supported. // // allocator_type // // // Constructors // Map(const Freeze::ConnectionPtr& connection, const std::string& dbName, bool createDb = true, const Compare& compare = Compare()) : _communicator(connection->getCommunicator()) { KeyCompareBasePtr keyCompare = new KeyCompare(compare, _communicator); std::vector indices; _helper.reset(MapHelper::create(connection, dbName, KeyCodec::typeId(), ValueCodec::typeId(), keyCompare, indices, createDb)); } template Map(const Freeze::ConnectionPtr& connection, const std::string& dbName, bool createDb, _InputIterator first, _InputIterator last, const Compare& compare = Compare()) : _communicator(connection->getCommunicator()) { KeyCompareBasePtr keyCompare = new KeyCompare(compare, _communicator); std::vector indices; _helper.reset(MapHelper::create(connection, dbName, KeyCodec::typeId(), ValueCodec::typeId(), keyCompare, indices, createDb)); while(first != last) { put(*first); ++first; } } ~Map() { } static void recreate(const Freeze::ConnectionPtr& connection, const std::string& dbName, const Compare& compare = Compare()) { KeyCompareBasePtr keyCompare = new KeyCompare(compare, connection->getCommunicator()); std::vector indices; MapHelper::recreate(connection, dbName, KeyCodec::typeId(), ValueCodec::typeId(), keyCompare, indices); } bool operator==(const Map& rhs) const { // // This does a memberwise equality for the entire contents of // the database. While slow this is always correct. Database // equality is not necessarily correct in the context of a // transaction. // if(size() != rhs.size()) { return false; } for(const_iterator p = rhs.begin() ; p != rhs.end() ; ++p) { const_iterator q = rhs.find(p->first); if(q == rhs.end()) { return false; } if(p->second != q->second) { return false; } } return true; } bool operator!=(const Map& rhs) const { return !(*this == rhs); } void swap(Map& rhs) { MapHelper* tmp = _helper.release(); _helper.reset(rhs._helper.release()); rhs._helper.reset(tmp); Ice::CommunicatorPtr tmpCom = _communicator; _communicator = rhs._communicator; rhs._communicator = tmpCom; } iterator begin() { try { return iterator(IteratorHelper::create(*_helper.get(), false), _communicator); } catch(const NotFoundException&) { return iterator(); } } const_iterator begin() const { try { return const_iterator(IteratorHelper::create(*_helper.get(), true), _communicator); } catch(const NotFoundException&) { return const_iterator(); } } iterator end() { return iterator(); } const_iterator end() const { return const_iterator(); } bool empty() const { return size() == 0; } size_type size() const { return _helper->size(); } size_type max_size() const { return 0xffffffff; // TODO: is this the max? } // // This method isn't implemented. // // mapped_type& operator[](const key_type& key) // // // This method isn't in the STLport library - but it's referenced // in "STL Tutorial and Reference Guide, Second Edition". It's not // currently implemented. // // const mapped_type& operator[](const key_type& key) const; // // // No allocators. // //allocator_type get_allocator() const; // iterator insert(iterator /*position*/, const value_type& key) { // // position is ignored. // Key k; KeyCodec::write(key.first, k, _communicator); iterator r = iterator(_helper->find(k, false), _communicator); if(r == end()) { Value v; ValueCodec::write(key.second, v, _communicator); _helper->put(k, v); r = iterator(_helper->find(k, false), _communicator); } return r; } std::pair insert(const value_type& key) { Key k; KeyCodec::write(key.first, k, _communicator); iterator r = iterator(_helper->find(k, false), _communicator); bool inserted = false; if(r == end()) { Value v; ValueCodec::write(key.second, v, _communicator); _helper->put(k, v); inserted = true; r = iterator(_helper->find(k, false), _communicator); } return std::pair(r, inserted); } template void insert(InputIterator first, InputIterator last) { while(first != last) { insert(*first); ++first; } } void put(const value_type& key) { // // insert or replace // Key k; Value v; KeyCodec::write(key.first, k, _communicator); ValueCodec::write(key.second, v, _communicator); _helper->put(k, v); } template void put(InputIterator first, InputIterator last) { while(first != last) { put(*first); ++first; } } void erase(iterator position) { assert(position._helper.get() != 0); position._helper->erase(); } size_type erase(const key_type& key) { Key k; KeyCodec::write(key, k, _communicator); return _helper->erase(k); } void erase(iterator first, iterator last) { while(first != last) { first._helper->erase(); ++first; } } void clear() { _helper->clear(); } // // destroy is not a standard function // void destroy() { _helper->destroy(); } // // closeDb closes the underlying Berkeley DB database // void closeDb() { _helper->closeDb(); } iterator find(const key_type& key) { Key k; KeyCodec::write(key, k, _communicator); return iterator(_helper->find(k, false), _communicator); } const_iterator find(const key_type& key) const { Key k; KeyCodec::write(key, k, _communicator); return const_iterator(_helper->find(k, true), _communicator); } size_type count(const key_type& key) const { Key k; KeyCodec::write(key, k, _communicator); return _helper->count(k); } iterator lower_bound(const key_type& key) { Key k; KeyCodec::write(key, k, _communicator); return iterator(_helper->lowerBound(k, false), _communicator); } const_iterator lower_bound(const key_type& key) const { Key k; KeyCodec::write(key, k, _communicator); return iterator(_helper->lowerBound(k, true), _communicator); } iterator upper_bound(const key_type& key) { Key k; KeyCodec::write(key, k, _communicator); return iterator(_helper->upperBound(k, false), _communicator); } const_iterator upper_bound(const key_type& key) const { Key k; KeyCodec::write(key, k, _communicator); return iterator(_helper->upperBound(k, true), _communicator); } std::pair equal_range(const key_type& key) { return std::make_pair(lower_bound(key), upper_bound(key)); } std::pair equal_range(const key_type& key) const { return std::make_pair(lower_bound(key), upper_bound(key)); } const Ice::CommunicatorPtr& communicator() const { return _communicator; } // // getConnection returns the associated connection // ConnectionPtr getConnection() const { return _helper->getConnection(); } protected: Map(const Ice::CommunicatorPtr& communicator) : _communicator(communicator) { } std::auto_ptr _helper; Ice::CommunicatorPtr _communicator; }; } // // This is for MSVC. // # ifdef _STLP_USE_OLD_HP_ITERATOR_QUERIES namespace std { // TODO: update. template inline pair* value_type(const Freeze::Iterator&) { return (pair*)0; } template inline pair* value_type(const Freeze::ConstIterator&) { return (pair*)0; } inline forward_iterator_tag iterator_category(const Freeze::IteratorBase&) { return forward_iterator_tag(); } inline ptrdiff_t* distance_type(const Freeze::IteratorBase&) { return (ptrdiff_t*) 0; } } #endif #endif