// ********************************************************************** // // Copyright (c) 2003-2016 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; class Dbt; 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; Ice::EncodingVersion _encoding; 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* find(const Dbt&, 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 void put(const Dbt&, const Dbt&) = 0; virtual size_t erase(const Key&) = 0; virtual size_t erase(const Dbt&) = 0; virtual size_t count(const Key&) const = 0; virtual size_t count(const Dbt&) 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() ICE_NOEXCEPT_FALSE = 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 set(const Dbt&) = 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 explicitly 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(IteratorHelper* helper, const Ice::CommunicatorPtr& communicator, const Ice::EncodingVersion& encoding) : _helper(helper), _communicator(communicator), _encoding(encoding), _refValid(false) { } Iterator() : _encoding(Ice::Encoding_1_0), _refValid(false) { } Iterator(const Iterator& rhs) : _communicator(rhs._communicator), _encoding(rhs._encoding), _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; _encoding = rhs._encoding; _refValid = false; } return *this; } ~Iterator() ICE_NOEXCEPT_FALSE { } 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()); ValueCodec v(value, _communicator, _encoding); _helper->set(v.dbt()); _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, _encoding); ValueCodec::read(value, *v, _communicator, _encoding); } friend class ConstIterator; friend class Map; IceUtil::UniquePtr _helper; Ice::CommunicatorPtr _communicator; Ice::EncodingVersion _encoding; // // 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(IteratorHelper* helper, const Ice::CommunicatorPtr& communicator, const Ice::EncodingVersion& encoding) : _helper(helper), _communicator(communicator), _encoding(encoding), _refValid(false) { } ConstIterator() : _encoding(Ice::Encoding_1_0), _refValid(false) { } ConstIterator(const ConstIterator& rhs) : _communicator(rhs._communicator), _encoding(rhs._encoding), _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) : _communicator(rhs._communicator), _encoding(rhs._encoding), _refValid(false) { if(rhs._helper.get() != 0) { _helper.reset(rhs._helper->clone()); } } ConstIterator& operator=(const ConstIterator& rhs) { if(this != &rhs) { if(rhs._helper.get() != 0) { _helper.reset(rhs._helper->clone()); } else { _helper.reset(); } _communicator = rhs._communicator; _encoding = rhs._encoding; _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; _encoding = rhs._encoding; _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, _encoding); ValueCodec::read(value, *v, _communicator, _encoding); } friend class Map; IceUtil::UniquePtr _helper; Ice::CommunicatorPtr _communicator; Ice::EncodingVersion _encoding; // // 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; }; struct IceEncodingCompare {}; template class KeyCompare : public KeyCompareBase { public: KeyCompare(const Compare& mapCompare, const Ice::CommunicatorPtr& communicator, const Ice::EncodingVersion& encoding) : KeyCompareBase(true), _compare(mapCompare), _communicator(communicator), _encoding(encoding) {} virtual int compare(const Key& dbKey1, const Key& dbKey2) { key_type key1; KeyCodec::read(key1, dbKey1, _communicator, _encoding); key_type key2; KeyCodec::read(key2, dbKey2, _communicator, _encoding); if(_compare(key1, key2)) { return -1; } else if(_compare(key2, key1)) { return 1; } else { return 0; } } private: Compare _compare; const Ice::CommunicatorPtr _communicator; const Ice::EncodingVersion _encoding; }; // // Partial template specialization: // do nothing for the IceEncodingCompare comparator // template class KeyCompare : public KeyCompareBase { public: KeyCompare(const IceEncodingCompare&, const Ice::CommunicatorPtr&, const Ice::EncodingVersion&): KeyCompareBase(false) {} virtual int compare(const Key&, const Key&) { assert(0); return 0; } }; // // 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, _encoding); key_type key2; KeyCodec::read(key2, dbKey2, _communicator, _encoding); if(_compare(key1, key2)) { return -1; } else if(_compare(key2, key1)) { return 1; } else { return 0; } } protected: MapIndex(const std::string& mapName, const Compare& mapCompare) : MapIndexBase(mapName, true), _compare(mapCompare) {} private: Compare _compare; }; // // 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& mapName, const IceEncodingCompare&): MapIndexBase(mapName, false) {} }; class FREEZE_API MapCodecBase { public: const Dbt& dbt() const { return *_dbt; } protected: MapCodecBase(const Ice::CommunicatorPtr&, const Ice::EncodingVersion&); ~MapCodecBase(); void init(); IceInternal::BasicStream _stream; Dbt* _dbt; }; // // Codec template for all key types. Marshaled keys are NOT encapsulated. // template class MapKeyCodec : public MapCodecBase { public: // // Use the constructor to marshal a value while avoiding unnecessary copies of the // marshaled data. The inherited dbt() accessor provides a Dbt value initialized with // the marshaled bytes. // MapKeyCodec(const T& v, const Ice::CommunicatorPtr& communicator, const Ice::EncodingVersion& encoding) : MapCodecBase(communicator, encoding) { _stream.write(v); init(); } template static void write(const U& v, std::vector& bytes, const Ice::CommunicatorPtr& communicator, const Ice::EncodingVersion& encoding) { IceInternal::BasicStream stream(IceInternal::getInstance(communicator).get(), encoding); stream.write(v); std::vector(stream.b.begin(), stream.b.end()).swap(bytes); } template static void read(U& v, const std::vector& bytes, const Ice::CommunicatorPtr& communicator, const Ice::EncodingVersion& encoding) { IceInternal::BasicStream stream(IceInternal::getInstance(communicator).get(), encoding, &bytes[0], &bytes[0] + bytes.size()); stream.read(v); } }; // // Codec template for all value types except those that use classes. Marshaled values are encapsulated. // template class MapValueCodec : public MapCodecBase { public: // // Use the constructor to marshal a value while avoiding unnecessary copies of the // marshaled data. The inherited dbt() accessor provides a Dbt value initialized with // the marshaled bytes. // MapValueCodec(const T& v, const Ice::CommunicatorPtr& communicator, const Ice::EncodingVersion& encoding) : MapCodecBase(communicator, encoding) { _stream.startWriteEncaps(); _stream.write(v); _stream.endWriteEncaps(); init(); } template static void write(const U& v, std::vector& bytes, const Ice::CommunicatorPtr& communicator, const Ice::EncodingVersion& encoding) { IceInternal::BasicStream stream(IceInternal::getInstance(communicator).get(), encoding); stream.startWriteEncaps(); stream.write(v); stream.endWriteEncaps(); std::vector(stream.b.begin(), stream.b.end()).swap(bytes); } template static void read(U& v, const std::vector& bytes, const Ice::CommunicatorPtr& communicator, const Ice::EncodingVersion& encoding) { IceInternal::BasicStream stream(IceInternal::getInstance(communicator).get(), encoding, &bytes[0], &bytes[0] + bytes.size()); stream.startReadEncaps(); stream.read(v); stream.endReadEncaps(); } }; // // Codec template for all value types that use classes. Marshaled values are encapsulated. // template class MapObjectValueCodec : public MapCodecBase { public: // // Use the constructor to marshal a value while avoiding unnecessary copies of the // marshaled data. The inherited dbt() accessor provides a Dbt value initialized with // the marshaled bytes. // MapObjectValueCodec(const T& v, const Ice::CommunicatorPtr& communicator, const Ice::EncodingVersion& encoding) : MapCodecBase(communicator, encoding) { _stream.startWriteEncaps(); _stream.write(v); _stream.writePendingObjects(); _stream.endWriteEncaps(); init(); } template static void write(const U& v, std::vector& bytes, const Ice::CommunicatorPtr& communicator, const Ice::EncodingVersion& encoding) { IceInternal::BasicStream stream(IceInternal::getInstance(communicator).get(), encoding); stream.startWriteEncaps(); stream.write(v); stream.writePendingObjects(); stream.endWriteEncaps(); std::vector(stream.b.begin(), stream.b.end()).swap(bytes); } template static void read(U& v, const std::vector& bytes, const Ice::CommunicatorPtr& communicator, const Ice::EncodingVersion& encoding) { IceInternal::BasicStream stream(IceInternal::getInstance(communicator).get(), encoding, &bytes[0], &bytes[0] + bytes.size()); stream.sliceObjects(false); stream.startReadEncaps(); stream.read(v); stream.readPendingObjects(); stream.endReadEncaps(); } }; // // 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, const std::string& keyTypeId, const std::string& valueTypeId, bool createDb = true, const Compare& compare = Compare()) : _communicator(connection->getCommunicator()), _encoding(connection->getEncoding()) { KeyCompareBasePtr keyCompare = new KeyCompare(compare, _communicator, _encoding); std::vector indices; _helper.reset(MapHelper::create(connection, dbName, keyTypeId, valueTypeId, keyCompare, indices, createDb)); } template Map(const Freeze::ConnectionPtr& connection, const std::string& dbName, const std::string& keyTypeId, const std::string& valueTypeId, bool createDb, _InputIterator first, _InputIterator last, const Compare& compare = Compare()) : _communicator(connection->getCommunicator()), _encoding(connection->getEncoding()) { KeyCompareBasePtr keyCompare = new KeyCompare(compare, _communicator, _encoding); std::vector indices; _helper.reset(MapHelper::create(connection, dbName, keyTypeId, valueTypeId, 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(), // connection->getEncoding()); // 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; Ice::EncodingVersion tmpEnc = _encoding; _communicator = rhs._communicator; _encoding = rhs._encoding; rhs._communicator = tmpCom; rhs._encoding = tmpEnc; } iterator begin() { try { return iterator(IteratorHelper::create(*_helper.get(), false), _communicator, _encoding); } catch(const NotFoundException&) { return iterator(); } } const_iterator begin() const { try { return const_iterator(IteratorHelper::create(*_helper.get(), true), _communicator, _encoding); } 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. // KeyCodec k(key.first, _communicator, _encoding); iterator r = iterator(_helper->find(k.dbt(), false), _communicator, _encoding); if(r == end()) { ValueCodec v(key.second, _communicator, _encoding); _helper->put(k.dbt(), v.dbt()); r = iterator(_helper->find(k.dbt(), false), _communicator, _encoding); } return r; } std::pair insert(const value_type& key) { KeyCodec k(key.first, _communicator, _encoding); iterator r = iterator(_helper->find(k.dbt(), false), _communicator, _encoding); bool inserted = false; if(r == end()) { ValueCodec v(key.second, _communicator, _encoding); _helper->put(k.dbt(), v.dbt()); inserted = true; r = iterator(_helper->find(k.dbt(), false), _communicator, _encoding); } 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 // KeyCodec k(key.first, _communicator, _encoding); ValueCodec v(key.second, _communicator, _encoding); _helper->put(k.dbt(), v.dbt()); } 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) { KeyCodec k(key, _communicator, _encoding); return _helper->erase(k.dbt()); } 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) { KeyCodec k(key, _communicator, _encoding); return iterator(_helper->find(k.dbt(), false), _communicator, _encoding); } const_iterator find(const key_type& key) const { KeyCodec k(key, _communicator, _encoding); return const_iterator(_helper->find(k.dbt(), true), _communicator, _encoding); } size_type count(const key_type& key) const { KeyCodec k(key, _communicator, _encoding); return _helper->count(k.dbt()); } iterator lower_bound(const key_type& key) { Key k; KeyCodec::write(key, k, _communicator, _encoding); return iterator(_helper->lowerBound(k, false), _communicator, _encoding); } const_iterator lower_bound(const key_type& key) const { Key k; KeyCodec::write(key, k, _communicator, _encoding); return iterator(_helper->lowerBound(k, true), _communicator, _encoding); } iterator upper_bound(const key_type& key) { Key k; KeyCodec::write(key, k, _communicator, _encoding); return iterator(_helper->upperBound(k, false), _communicator, _encoding); } const_iterator upper_bound(const key_type& key) const { Key k; KeyCodec::write(key, k, _communicator, _encoding); return iterator(_helper->upperBound(k, true), _communicator, _encoding); } 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& mapCommunicator, const Ice::EncodingVersion& encoding) : _communicator(mapCommunicator), _encoding(encoding) { } IceUtil::UniquePtr _helper; Ice::CommunicatorPtr _communicator; Ice::EncodingVersion _encoding; }; } #endif