diff options
Diffstat (limited to 'cpp/include/Freeze/Map.h')
-rw-r--r-- | cpp/include/Freeze/Map.h | 966 |
1 files changed, 966 insertions, 0 deletions
diff --git a/cpp/include/Freeze/Map.h b/cpp/include/Freeze/Map.h new file mode 100644 index 00000000000..b8094a954aa --- /dev/null +++ b/cpp/include/Freeze/Map.h @@ -0,0 +1,966 @@ +// ********************************************************************** +// +// Copyright (c) 2001 +// MutableRealms, Inc. +// Huntsville, AL, USA +// +// All Rights Reserved +// +// ********************************************************************** + +#ifndef FREEZE_MAP_H +#define FREEZE_MAP_H + +#include <Freeze/DB.h> +#include <Ice/Ice.h> + +#include <iterator> + +namespace Freeze +{ + +// +// This wrapper class holds a key/value pair and the associated +// database. Upon assignment it writes the new value into the backing +// database. +// +template <typename key_type, typename value_type, typename KeyCodec, typename ValueCodec> +class DBWrapper +{ +public: + + DBWrapper() + { + } + + DBWrapper(const DBPtr& db, const key_type& key, const value_type& value) : + _db(db), _key(key), _value(value) + { + } + + operator value_type() const + { + return _value; + } + + bool operator!=(const value_type& value) const + { + return _value != value; + } + + bool operator==(const value_type& value) const + { + return _value == value; + } + + DBWrapper& operator=(const value_type& value) + { + Freeze::Key k; + Freeze::Value v; + + IceInternal::InstancePtr instance = IceInternal::getInstance(_db->getCommunicator()); + k = KeyCodec::write(_key, instance); + v = ValueCodec::write(value, instance); + + _db->put(k, v); + + _value = value; + + return *this; + } + +private: + + DBPtr _db; + key_type _key; + value_type _value; +}; + +// +// Forward declaration +// +template <typename key_type, typename mapped_type, typename KeyCodec, typename ValueCodec> +class DBMap; +template <typename key_type, typename mapped_type, typename KeyCodec, typename ValueCodec> +class ConstDBIterator; + +// +// It's necessary to have DBPair so that automatic conversion to +// std::pair<key_type, mapped_type> from std::pair<key_type, +// DBWrapper<...> > can work correctly (and global operator== & +// operator!=). +// +template <typename key_type, typename mapped_type, typename KeyCodec, typename ValueCodec> +struct DBPair +{ + typedef key_type first_type; + typedef DBWrapper<key_type, mapped_type, KeyCodec, ValueCodec> second_type; + + first_type first; + second_type second; + + DBPair() : + first(first_type()), second(second_type()) + { + } + + DBPair(const first_type& f, const second_type& s) : + first(f), second(s) + { + } + + ~DBPair() + { + } + + operator std::pair<key_type, mapped_type>() + { + return std::pair<key_type, mapped_type>(first, second); + } +}; + +// +// Global operator== & operator!= for both DBPair & std::pair. +// +template <typename key_type, typename mapped_type, typename KeyCodec, typename ValueCodec> +inline bool operator==(const DBPair<key_type, mapped_type, KeyCodec, ValueCodec>& p1, + const DBPair<key_type, mapped_type, KeyCodec, ValueCodec>& p2) +{ + return p1.first == p2.first && p1.second == p2.second; +} + +template <typename key_type, typename mapped_type, typename KeyCodec, typename ValueCodec> +inline bool operator==(const DBPair<key_type, mapped_type, KeyCodec, ValueCodec>& p1, + const std::pair<key_type, mapped_type>& p2) +{ + return p1.first == p2.first && p1.second == p2.second; +} + +template <typename key_type, typename mapped_type, typename KeyCodec, typename ValueCodec> +inline bool operator==(const std::pair<key_type, mapped_type>& p1, + const DBPair<key_type, mapped_type, KeyCodec, ValueCodec>& p2) +{ + return p1.first == p2.first && p1.second == p2.second; +} + +template <typename key_type, typename mapped_type, typename KeyCodec, typename ValueCodec> +inline bool operator!=(const DBPair<key_type, mapped_type, KeyCodec, ValueCodec>& p1, + const DBPair<key_type, mapped_type, KeyCodec, ValueCodec>& p2) +{ + return p1.first != p2.first || p1.second != p2.second; +} + +template <typename key_type, typename mapped_type, typename KeyCodec, typename ValueCodec> +inline bool operator!=(const DBPair<key_type, mapped_type, KeyCodec, ValueCodec>& p1, + const std::pair<key_type, mapped_type>& p2) +{ + return p1.first != p2.first || p1.second != p2.second; +} + +template <typename key_type, typename mapped_type, typename KeyCodec, typename ValueCodec> +inline bool operator!=(const std::pair<key_type, mapped_type>& p1, + const DBPair<key_type, mapped_type, KeyCodec, ValueCodec>& p2) +{ + return p1.first != p2.first || p1.second != p2.second; +} + +// +// This is necessary for MSVC support. +// +struct DBIteratorBase +{ + typedef std::forward_iterator_tag iterator_category; +}; + +// +// Database iterator. This implements a forward iterator. +// +// Equality and inequality are based on whether the iterator is +// "valid". An valid iterator contains a valid database and cursor +// pointer, otherwise the iterator is invalid. +// +// TODO: It's possible to implement bidirectional iterators, if +// necessary. +// +template <typename key_type, typename mapped_type, typename KeyCodec, typename ValueCodec> +class DBIterator : public DBIteratorBase +{ +public: + + typedef ptrdiff_t difference_type; + + // + // NOTE: + // + // Normally this would be const key_type, mapped_type. However, + // since there the returned data is always data that is in the + // backing database then it's not necessary - in fact, it screws + // up the operator*() and operator-> methods. + // + typedef std::pair<key_type, mapped_type> value_type; + + typedef value_type* pointer; + + typedef value_type& reference; + + // + // This is a special value-type that allows write-back to the + // database. It's necessary to use DBPair so that automatic + // conversion to std::pair<key_type,mapped_type> can work correct + // (and global operator== & !=). + // + typedef DBPair<key_type, mapped_type, KeyCodec, ValueCodec> reference_value_type; + + DBIterator(const DBPtr& db, const DBCursorPtr& cursor) + : _db(db), _cursor(cursor) + { + } + + DBIterator() + { + } + + DBIterator(const DBIterator& rhs) + { + if (rhs._cursor) + { + _cursor = rhs._cursor->clone(); + } + + _db = rhs._db; + } + + DBIterator& operator=(const DBIterator& rhs) + { + if (_cursor) + { + _cursor->close(); + } + + if (rhs._cursor) + { + _cursor = rhs._cursor->clone(); + } + + _db = rhs._db; + + return *this; + } + + ~DBIterator() + { + if (_cursor) + { + _cursor->close(); + } + } + + bool operator==(const DBIterator& rhs) + { + if (!_db && !rhs._db) + { + return true; + } + return false; + } + + bool operator!=(const DBIterator& rhs) + { + return !(*this == rhs); + } + + DBIterator& operator++() + { + incr(); + return *this; + } + + DBIterator operator++(int) + { + DBIterator tmp = *this; + tmp.incr(); + return tmp; + } + + // + // Note that this doesn't follow the regular iterator mapping: + // + // value_type& operator*(), value_type operator*() const + // + reference_value_type& operator*() const + { + key_type key; + mapped_type value; + + getCurrentValue(key, value); + _ref = reference_value_type(key, DBWrapper<key_type, mapped_type, KeyCodec, ValueCodec> (_db, key, value)); + return _ref; + } + + // + // Special version that allows writing back to the database. + // + reference_value_type* operator->() { return &(operator*()); } + +private: + + void incr() + { + assert(_cursor && _db); + if (!_cursor->next()) + { + // + // The iterator has been moved past the end, and is now + // invalid. + // + _cursor->close(); + _cursor = 0; + _db = 0; + } + } + + void getCurrentValue(key_type& key, mapped_type& value) const + { + Freeze::Key k; + Freeze::Value v; + + _cursor->curr(k, v); + + IceInternal::InstancePtr instance = IceInternal::getInstance(_db->getCommunicator()); + KeyCodec::read(key, k, instance); + ValueCodec::read(value, v, instance); + } + + friend class ConstDBIterator<key_type, mapped_type, KeyCodec, ValueCodec>; + friend class DBMap<key_type, mapped_type, KeyCodec, ValueCodec>; + + DBPtr _db; + DBCursorPtr _cursor; + + // + // Cached last return value. This is so that operator->() can + // actually return a pointer. + // + mutable reference_value_type _ref; +}; + +// +// See DBIterator comments for design notes +// +template <typename key_type, typename mapped_type, typename KeyCodec, typename ValueCodec> +class ConstDBIterator : public DBIteratorBase +{ +public: + + typedef ptrdiff_t difference_type; + + // + // NOTE: + // + // Normally this would be const key_type, mapped_type. However, + // since there the returned data is always data that is in the + // backing database then it's not necessary - in fact, it screws + // up the operator*() and operator-> methods. + // + typedef std::pair<key_type, mapped_type> value_type; + + typedef value_type* pointer; + + typedef value_type& reference; + + ConstDBIterator(const DBPtr& db, const DBCursorPtr& cursor) + : _db(db), _cursor(cursor) + { + } + ConstDBIterator() { } + + ConstDBIterator(const ConstDBIterator& rhs) + { + if (rhs._cursor) + { + _cursor = rhs._cursor->clone(); + } + + _db = rhs._db; + } + + // + // A DBIterator can be converted to a ConstDBIterator (but not + // vice versa) - same for operator=. + // + ConstDBIterator(const DBIterator<key_type, mapped_type, KeyCodec, ValueCodec>& rhs) + { + if (rhs._cursor) + { + _cursor = rhs._cursor->clone(); + } + + _db = rhs._db; + } + + ConstDBIterator& operator=(const ConstDBIterator& rhs) + { + if (_cursor) + { + _cursor->close(); + } + + if (rhs._cursor) + { + _cursor = rhs._cursor->clone(); + } + + _db = rhs._db; + + return *this; + } + + // + // Create const_iterator from iterator. + // + ConstDBIterator& operator=(const DBIterator<key_type, mapped_type, KeyCodec, ValueCodec>& rhs) + { + if (_cursor) + { + _cursor->close(); + } + + if (rhs._cursor) + { + _cursor = rhs._cursor->clone(); + } + + _db = rhs._db; + + return *this; + } + + ~ConstDBIterator() + { + if (_cursor) + { + _cursor->close(); + } + } + + bool operator==(const ConstDBIterator& rhs) + { + if (!_db && !rhs._db) + { + return true; + } + return false; + } + + bool operator!=(const ConstDBIterator& rhs) + { + return !(*this == rhs); + } + + ConstDBIterator& operator++() + { + incr(); + return *this; + } + + ConstDBIterator operator++(int) + { + ConstDBIterator tmp = *this; + tmp.incr(); + return tmp; + } + + value_type& operator*() const + { + key_type key; + mapped_type value; + + getCurrentValue(key, value); + _ref = value_type(key, value); + return _ref; + } + + pointer operator->() const { return &(operator*()); } + +private: + + void incr() + { + assert(_cursor); + if (!_cursor->next()) + { + // + // The iterator has been moved past the end, and is now + // invalid. + // + _cursor->close(); + _cursor = 0; + _db = 0; + } + } + + void getCurrentValue(key_type& key, mapped_type& value) const + { + Freeze::Key k; + Freeze::Value v; + + _cursor->curr(k, v); + + IceInternal::InstancePtr instance = IceInternal::getInstance(_db->getCommunicator()); + KeyCodec::read(key, k, instance); + ValueCodec::read(value, v, instance); + } + + friend class DBMap<key_type, mapped_type, KeyCodec, ValueCodec>; + + DBPtr _db; + DBCursorPtr _cursor; + + // + // Cached last return value. This is so that operator->() can + // actually return a pointer. + // + mutable value_type _ref; +}; + +// +// This is an STL container that matches the requirements of a +// Associated Container. It also supports the same interface as a +// Hashed Associative Container, except the hasher & key_equal +// methods. +// +// TODO: If necessary it would be possible to implement reverse and +// bidirectional iterators. +// +template <typename key_type, typename mapped_type, typename KeyCodec, typename ValueCodec> +class DBMap +{ +public: + + // + // NOTE: + // + // Normally this would be const key_type, mapped_type. However, + // since there the returned data is always data that is in the + // backing database then it's not necessary - in fact, it screws + // up the operator*() and operator-> methods. + // + typedef std::pair<key_type, mapped_type> value_type; + + // + // These are not supported: + // + // hasher, key_equal, key_compare, value_compare + // + + typedef DBIterator<key_type, mapped_type, KeyCodec, ValueCodec > iterator; + typedef ConstDBIterator<key_type, mapped_type, KeyCodec, ValueCodec > const_iterator; + + typedef std::pair<const key_type&, mapped_type>& reference; + typedef const std::pair<const key_type&, mapped_type>& const_reference; + + typedef std::pair<const key_type&, mapped_type>* pointer; + typedef const std::pair<const key_type&, mapped_type>* const_pointer; + + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + // + // Special type similar to DBIterator::value_type_reference + // + typedef DBWrapper<key_type, mapped_type, KeyCodec, ValueCodec> mapped_type_reference; + + // + // Allocators are not supported. + // + // allocator_type + + // + // Constructors + // + DBMap(const DBPtr& db) : + _db(db) + { + } + +#ifdef __STL_MEMBER_TEMPLATES + template <class _InputIterator> + DBMap(const DBPtr& db, _InputIterator first, _InputIterator last) : + _db(db) + { + while (first != last) + { + insert(*first); + ++first; + } + } +#else + DBMap(const DBPtr& db, const value_type* first, const value_type* last) : + _db(db) + { + while (first != last) + { + insert(*first); + ++first; + } + } + DBMap(const DBPtr& db, const_iterator first, const_iterator last) : + _db(db) + { + while (first != last) + { + insert(*first); + ++first; + } + } +#endif /*__STL_MEMBER_TEMPLATES */ + + ~DBMap() + { + } + + // + // Neither of these operations are supported. + // + // key_compare key_comp() const, value_compare value_comp() const, + // hasher hash_funct() const, key_equal key_eq() const + // + + bool operator==(const DBMap& 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 (count() != rhs.count()) + 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 DBMap& rhs) const + { + return !(*this == rhs); + } + + void swap(DBMap& rhs) + { + DBPtr tmp = _db; + _db = rhs._db; + rhs._db = tmp; + } + + iterator begin() + { + try + { + return iterator(_db, _db->getCursor()); + } + catch(const DBNotFoundException&) + { + return iterator(); + } + } + const_iterator begin() const + { + try + { + return const_iterator(_db, _db->getCursor()); + } + catch(const DBNotFoundException&) + { + 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 (size_type)_db->getNumberOfRecords(); + } + + size_type max_size() const + { + return 0xffffffff; // TODO: is this the max? + } + + mapped_type_reference operator[](const key_type& key) + { + IceInternal::InstancePtr instance = IceInternal::getInstance(_db->getCommunicator()); + Freeze::Key k = KeyCodec::write(key, instance); + mapped_type value; + + try + { + Freeze::Value v = _db->get(k); + ValueCodec::read(value, v, instance); + } + catch(const DBNotFoundException&) + { + value = mapped_type(); + Freeze::Value v = ValueCodec::write(value, instance); + _db->put(k, v); + } + return mapped_type_reference(_db, key, value); + + } + + // + // This method isn't in the STLport library - but it's referenced + // in "STL Tutorial and Refrence 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. + // + IceInternal::InstancePtr instance = IceInternal::getInstance(_db->getCommunicator()); + + Freeze::Key k; + Freeze::Value v; + k = KeyCodec::write(key.first, instance); + v = ValueCodec::write(key.second, instance); + + _db->put(k, v); + DBCursorPtr cursor = _db->getCursorAtKey(k); + + return iterator(_db, cursor); + } + + std::pair<iterator, bool> insert(const value_type& key) + { + IceInternal::InstancePtr instance = IceInternal::getInstance(_db->getCommunicator()); + + Freeze::Key k; + Freeze::Value v; + k = KeyCodec::write(key.first, instance); + v = ValueCodec::write(key.second, instance); + + DBCursorPtr cursor; + bool inserted; + + try + { + // + // Does the value exist already? + // + cursor = _db->getCursorAtKey(k); + inserted = false; + } + catch(const DBNotFoundException&) + { + inserted = true; + } + + _db->put(k, v); + if (inserted) + { + cursor = _db->getCursorAtKey(k); + } + return std::pair<iterator, bool>(iterator(_db, cursor), inserted); + } + +#ifdef __STL_MEMBER_TEMPLATES + template <typename InputIterator> + void insert(InputIterator first, InputIterator last) + { + while (first != last) + { + insert(*first); + ++first; + } + } +#else + void insert(const value_type* first, const value_type* last) + { + while (first != last) + { + insert(*first); + ++first; + } + } + void insert(const_iterator first, const_iterator last) + { + while (first != last) + { + insert(*first); + ++first; + } + } +#endif + + void erase(iterator position) + { + position._cursor->del(); + } + + size_type erase(const key_type& key) + { + IceInternal::InstancePtr instance = IceInternal::getInstance(_db->getCommunicator()); + + Freeze::Key k = KeyCodec::write(key, instance); + + try + { + _db->del(k); + } + catch(const DBNotFoundException&) + { + return 0; + } + + return 1; + } + + void erase(iterator first, iterator last) + { + while (first != last) + { + first._cursor->del(); + ++first; + } + } + + void clear() + { + _db->clear(); + } + + iterator find(const key_type& key) + { + IceInternal::InstancePtr instance = IceInternal::getInstance(_db->getCommunicator()); + + Freeze::Key k; + k = KeyCodec::write(key, instance); + + try + { + DBCursorPtr cursor = _db->getCursorAtKey(k); + return iterator(_db, cursor); + } + catch(const DBNotFoundException&) + { + // + // The record doesn't exist, return the end() iterator. + // + } + return end(); + } + + const_iterator find(const key_type& key) const + { + IceInternal::InstancePtr instance = IceInternal::getInstance(_db->getCommunicator()); + + Freeze::Key k; + k = KeyCodec::write(key, instance); + + try + { + DBCursorPtr cursor = _db->getCursorAtKey(k); + return const_iterator(_db, cursor); + } + catch(const DBNotFoundException&) + { + // + // The record doesn't exist, return the end() iterator. + // + } + return end(); + } + + size_type count(const key_type& key) const + { + if (find(key) != end()) + return 1; + return 0; + } + + std::pair<iterator, iterator> equal_range(const key_type& key) + { + iterator p = find(key); + return std::pair<iterator,iterator>(p,p); + } + + std::pair<const_iterator, const_iterator> equal_range(const key_type& key) const + { + const_iterator p = find(key); + return std::pair<const_iterator,const_iterator>(p,p); + } + +private: + + DBPtr _db; +}; + +} // End namespace Freeze + +// +// This is for MSVC. +// +# ifdef _STLP_USE_OLD_HP_ITERATOR_QUERIES +namespace std +{ + +template <class key_type, class mapped_type, class KeyCodec, class ValueCodec> +inline pair<key_type, mapped_type>* +value_type(const Freeze::DBIterator<key_type, mapped_type, KeyCodec, ValueCodec>&) +{ + return (pair<key_type, mapped_type>*)0; +} + +template <class key_type, class mapped_type, class KeyCodec, class ValueCodec> +inline pair<key_type, mapped_type>* +value_type(const Freeze::ConstDBIterator<key_type, mapped_type, KeyCodec, ValueCodec>&) +{ + return (pair<key_type, mapped_type>*)0; +} + +inline forward_iterator_tag iterator_category(const Freeze::DBIteratorBase&) +{ + return forward_iterator_tag(); +} + +inline ptrdiff_t* distance_type(const Freeze::DBIteratorBase&) { return (ptrdiff_t*) 0; } + +} // End namespace std + +#endif /* _STLP_CLASS_PARTIAL_SPECIALIZATION */ + +#endif |