diff options
Diffstat (limited to 'stl/dbstl_vector.h')
-rw-r--r-- | stl/dbstl_vector.h | 3332 |
1 files changed, 3332 insertions, 0 deletions
diff --git a/stl/dbstl_vector.h b/stl/dbstl_vector.h new file mode 100644 index 0000000..cf26484 --- /dev/null +++ b/stl/dbstl_vector.h @@ -0,0 +1,3332 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#ifndef _DB_STL_DB_VECTOR_H +#define _DB_STL_DB_VECTOR_H + +#include "dbstl_common.h" +#include "dbstl_dbc.h" +#include "dbstl_element_ref.h" +#include "dbstl_resource_manager.h" +#include "dbstl_container.h" +#include "dbstl_base_iterator.h" +#include <list> +#include <algorithm> + +START_NS(dbstl) + +using std::list; +using std::istream; +using std::ostream; +using std::sort; + +#define TRandDbCursor RandDbCursor<T> + +// Forward Declarations +// The default parameter is needed for the following code to work. +template <class T, Typename value_type_sub = ElementRef<T> > +class db_vector; +template <class T, Typename value_type_sub = ElementRef<T> > +class db_vector_iterator; +template<class kdt, class ddt> class DbCursor; +template<class ddt> class RandDbCursor; +template<class ddt> class ElementRef; +template <typename T, typename T2> +class DbstlListSpecialOps; + +/// \ingroup dbstl_iterators +/// @{ +/// \defgroup db_vector_iterators Iterator classes for db_vector. +/// db_vector has two iterator classes --- db_vector_base_iterator and +/// db_vector_iterator. The differences +/// between the two classes are that the db_vector_base_iterator +/// can only be used to read its referenced value, so it is intended as +/// db_vector's const iterator; While the other class allows both read and +/// write access. If your access pattern is readonly, it is strongly +/// recommended that you use the const iterator because it is faster +/// and more efficient. +/// The two classes have identical behaviors to std::vector::const_iterator and +/// std::vector::iterator respectively. Note that the common public member +/// function behaviors are described in the db_base_iterator section. +/// \sa db_base_iterator +//@{ +/////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////// +// +// db_vector_base_iterator class template definition +// +// db_vector_base_iterator is the const_iterator class for db_vector, and +// base class of db_vector_iterator -- iterator class for db_vector. Each +// db_vector_base_iterator object owns one RandDbCursor<> cursor (1:1 map). +// On copy construction, the cursor is not duplicated, it is only created +// when it is really used (ie: lazily). +// The value referred by this iterator is read only, can't be used to mutate +// its referenced data element. +// +/// This class is the const iterator class for db_vector, and it is +/// inheirted by the db_vector_iterator class, which is the iterator +/// class for db_vector. +template <class T> +class _exported db_vector_base_iterator : public db_base_iterator<T> +{ +protected: + // typedef's can't be put after where it is used. + typedef db_vector_base_iterator<T> self; + typedef db_recno_t index_type; + using db_base_iterator<T>::replace_current_key; +public: + //////////////////////////////////////////////////////////////////// + // + // Begin public type definitions. + // + typedef T value_type; + typedef ptrdiff_t difference_type; + typedef difference_type distance_type; + + /// This is the return type for operator[]. + /// \sa value_type_wrap operator[](difference_type _Off) const + typedef value_type value_type_wrap; + typedef value_type& reference; + typedef value_type* pointer; + // Use the STL tag, to ensure compatability with interal STL functions. + // + typedef std::random_access_iterator_tag iterator_category; + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // Begin public constructors and destructor. + /// \name Constructors and destroctor + /// Do not construct iterators explictily using these constructors, + /// but call db_vector::begin() const to get an valid iterator. + /// \sa db_vector::begin() const + //@{ + db_vector_base_iterator(const db_vector_base_iterator<T>& vi) + : base(vi), pcsr_(vi.pcsr_), curpair_base_(vi.curpair_base_) + { + + } + + explicit db_vector_base_iterator(db_container*powner, + u_int32_t b_bulk_retrieval = 0, bool rmw = false, + bool directdbget = true, bool readonly = false) + : base(powner, directdbget, readonly, b_bulk_retrieval, rmw), + pcsr_(new TRandDbCursor(b_bulk_retrieval, rmw, directdbget)) + { + + } + + db_vector_base_iterator() : pcsr_(NULL) + { + + } + + virtual ~db_vector_base_iterator() + { + this->dead_ = true; + if (pcsr_) + pcsr_->close(); + } + //@} + + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // + // Begin functions that compare iterator positions. + // + // Use itr_status_ to do comparison if it is non-zero because end + // iterator does not have an underlying key/data pair, and call + // underlying cursor comparison otherwise. + /// \name Iterator comparison operators + /// The way to compare two iterators is to compare the index values + /// of the two elements they point to. The iterator sitting on an + /// element with less index is regarded to be smaller. And the invalid + /// iterator sitting after last element is greater than any other + /// iterators, because it is assumed to have an index equal to last + /// element's index plus one; The invalid iterator sitting before first + /// element is less than any other iterators because it is assumed to + /// have an index -1. + //@{ + /// \brief Equality comparison operator. + /// + /// Invalid iterators are equal; Valid iterators + /// sitting on the same key/data pair equal; Otherwise not equal. + /// \param itr The iterator to compare against. + /// \return True if this iterator equals to itr; False otherwise. + inline bool operator==(const self&itr) const + { + COMPARE_CHECK(itr) + if ((itr.itr_status_ == this->itr_status_) && + (this->itr_status_ == INVALID_ITERATOR_POSITION)) + return true; + + if (this->itr_status_ != INVALID_ITERATOR_POSITION && + itr.itr_status_ != INVALID_ITERATOR_POSITION) { + return (pcsr_->compare((itr.pcsr_.base_ptr())) == 0); + } + return false; + } + + /// \brief Unequal compare, identical to !operator(==itr) + /// \param itr The iterator to compare against. + /// \return False if this iterator equals to itr; True otherwise. + inline bool operator!=(const self&itr) const + { + return !(*this == itr) ; + } + + // end() iterator is largest. If both are end() iterator return false. + /// \brief Less than comparison operator. + /// \param itr The iterator to compare against. + /// \return True if this iterator is less than itr. + inline bool operator < (const self& itr) const + { + bool ret; + + if (this == &itr) + return false; + + char after_last = base::IPT_AFTER_LAST, + bef_fst = base::IPT_BEFORE_FIRST; + + if (this->itr_status_ == INVALID_ITERATOR_POSITION && + this->inval_pos_type_ == after_last) + ret = false; + else if (this->itr_status_ == INVALID_ITERATOR_POSITION && + this->inval_pos_type_ == bef_fst) + ret = itr.inval_pos_type_ != bef_fst; + else { // This iterator is on an ordinary position. + if (itr.itr_status_ == INVALID_ITERATOR_POSITION && + itr.inval_pos_type_ == after_last) + ret = true; + else if (itr.itr_status_ == INVALID_ITERATOR_POSITION && + itr.inval_pos_type_ == bef_fst) + ret = false; + else { // Both iterators are on an ordinary position. + // By this stage all valid cases using + // INVALID_ITERATOR_POSITION have been dealt + // with. + assert((this->itr_status_ != + INVALID_ITERATOR_POSITION) && + (itr.itr_status_ != + INVALID_ITERATOR_POSITION)); + ret = pcsr_->compare( + (itr.pcsr_.base_ptr())) < 0; + } + } + return ret; + } + + /// \brief Less equal comparison operator. + /// \param itr The iterator to compare against. + /// \return True if this iterator is less than or equal to itr. + inline bool operator <= (const self& itr) const + { + return !(this->operator>(itr)); + } + + /// \brief Greater equal comparison operator. + /// \param itr The iterator to compare against. + /// \return True if this iterator is greater than or equal to itr. + inline bool operator >= (const self& itr) const + { + return !(this->operator<(itr)); + } + + // end() iterator is largest. If both are end() iterator return false. + /// \brief Greater comparison operator. + /// \param itr The iterator to compare against. + /// \return True if this iterator is greater than itr. + inline bool operator > (const self& itr) const + { + bool ret; + + if (this == &itr) + return false; + + char after_last = base::IPT_AFTER_LAST, + bef_fst = base::IPT_BEFORE_FIRST; + + if (this->itr_status_ == INVALID_ITERATOR_POSITION && + this->inval_pos_type_ == after_last) + ret = itr.inval_pos_type_ != after_last; + else if (this->itr_status_ == INVALID_ITERATOR_POSITION && + this->inval_pos_type_ == bef_fst) + ret = false; + else { // This iterator is on an ordinary position. + if (itr.itr_status_ == INVALID_ITERATOR_POSITION && + itr.inval_pos_type_ == after_last) + ret = false; + else if (itr.itr_status_ == INVALID_ITERATOR_POSITION && + itr.inval_pos_type_ == bef_fst) + ret = true; + else { // Both iterators are on an ordinary position. + // By this stage all valid cases using + // INVALID_ITERATOR_POSITION have been dealt + // with. + assert((this->itr_status_ != + INVALID_ITERATOR_POSITION) && + (itr.itr_status_ != + INVALID_ITERATOR_POSITION)); + ret = pcsr_->compare( + (itr.pcsr_.base_ptr())) > 0; + } + } + return ret; + } + //@} // vctitr_cmp + //////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////////////////////// + // + // Begin functions that shift the iterator position. + // + /// \name Iterator movement operators. + /// When we talk about iterator movement, we think the + /// container is a uni-directional range, represented by [begin, end), + /// and this is true no matter we are using iterators or reverse + /// iterators. When an iterator is moved closer to "begin", we say it + /// is moved forward, otherwise we say it is moved backward. + //@{ + /// \brief Pre-increment. + /// + /// Move the iterator one element backward, so that + /// the element it sits on has a bigger index. + /// Use ++iter rather than iter++ where possible to avoid two useless + /// iterator copy constructions. + /// \return This iterator after incremented. + inline self& operator++() + { + move_by(*this, 1, false); + return *this; + } + + /// \brief Post-increment. + /// Move the iterator one element backward, so that + /// the element it sits on has a bigger index. + /// Use ++iter rather than iter++ where possible to avoid two useless + /// iterator copy constructions. + /// \return A new iterator not incremented. + inline self operator++(int) + { + self itr(*this); + move_by(*this, 1, false); + + return itr; + } + + /// \brief Pre-decrement. + /// Move the iterator one element backward, so + /// that the element it sits on has a smaller index. + /// Use --iter rather than iter-- where possible to avoid two useless + /// iterator copy constructions. + /// \return This iterator after decremented. + inline self& operator--() + { + move_by(*this, 1, true); + return *this; + } + + /// \brief Post-decrement. + /// + /// Move the iterator one element backward, so + /// that the element it sits on has a smaller index. + /// Use --iter rather than iter-- where possible to avoid two useless + /// iterator copy constructions. + /// \return A new iterator not decremented. + inline self operator--(int) + { + self itr = *this; + move_by(*this, 1, true); + return itr; + } + + /// \brief Assignment operator. + /// + /// This iterator will point to the same key/data + /// pair as itr, and have the same configurations as itr. + /// \sa db_base_iterator::operator= + /// \param itr The right value of the assignment. + /// \return This iterator's reference. + inline const self& operator=(const self&itr) + { + ASSIGNMENT_PREDCOND(itr) + base::operator=(itr); + + if (pcsr_) + pcsr_->close(); + pcsr_ = itr.pcsr_; + curpair_base_ = itr.curpair_base_; + return itr; + } + + // Always move both iterator and cursor synchronously, keep iterators + // data synchronized. + /// Iterator movement operator. + /// Return another iterator by moving this iterator forward by n + /// elements. + /// \param n The amount and direction of movement. If negative, will + /// move forward by |n| element. + /// \return The new iterator at new position. + inline self operator+(difference_type n) const + { + self itr(*this); + move_by(itr, n, false); + return itr; + } + + /// \brief Move this iterator backward by n elements. + /// \param n The amount and direction of movement. If negative, will + /// move forward by |n| element. + /// \return Reference to this iterator at new position. + inline const self& operator+=(difference_type n) + { + move_by(*this, n, false); + return *this; + } + + /// \brief Iterator movement operator. + /// + /// Return another iterator by moving this iterator backward by n + /// elements. + /// \param n The amount and direction of movement. If negative, will + /// move backward by |n| element. + /// \return The new iterator at new position. + inline self operator-(difference_type n) const + { + self itr(*this); + move_by(itr, n); + + return itr; + } + + /// \brief Move this iterator forward by n elements. + /// \param n The amount and direction of movement. If negative, will + /// move backward by |n| element. + /// \return Reference to this iterator at new position. + inline const self& operator-=(difference_type n) + { + move_by(*this, n); + return *this; + } + //@} //itr_movement + + /// \brief Iterator distance operator. + /// + /// Return the index difference of this iterator and itr, so if this + /// iterator sits on an element with a smaller index, this call will + /// return a negative number. + /// \param itr The other iterator to substract. itr can be the invalid + /// iterator after last element or before first element, their index + /// will be regarded as last element's index + 1 and -1 respectively. + /// \return The index difference. + difference_type operator-(const self&itr) const + { + difference_type p1, p2; + + if (itr == end_itr_) { + if (itr.inval_pos_type_ == base::IPT_BEFORE_FIRST) + p2 = -1; + else if ( + this->inval_pos_type_ == base::IPT_AFTER_LAST) { + self pitr2(itr); + pitr2.open(); + pitr2.last(); + p2 = pitr2.get_current_index() + 1; + } else { + THROW0(InvalidIteratorException); + } + } else + p2 = itr.get_current_index(); + + if (*this == end_itr_) { + if (this->inval_pos_type_ == base::IPT_BEFORE_FIRST) + p1 = -1; + else if ( + this->inval_pos_type_ == base::IPT_AFTER_LAST) { + self pitr1(*this); + pitr1.open(); + pitr1.last(); + p1 = pitr1.get_current_index() + 1; + } else { + THROW0(InvalidIteratorException); + } + } else + p1 = this->get_current_index(); + + return (difference_type)(p1 - p2); + } + + //////////////////////////////////////////////////////////////////// + // + // Begin functions that retrieve values from the iterator. + // + // Each iterator has a dedicated cursor, and we keep the iterator + // and cursor synchronized all the time. The returned value is read + // only, can't be used to mutate its underlying data element. + // + /// \name Functions that retrieve values from the iterator. + //@{ + /// \brief Dereference operator. + /// + /// Return the reference to the cached data element, which is an + /// ElementRef<T> object if T is a class type or an ElementHolder<T> + /// object if T is a C++ primitive data type. + /// The returned value can only be used to read its referenced + /// element. + /// \return The reference to the element this iterator points to. + inline reference operator*() const + { + if (this->directdb_get_) + update_cur_pair(); + return curpair_base_; // Return reference, no copy construction. + } + + /// \brief Arrow operator. + /// + /// Return the pointer to the cached data element, which is an + /// ElementRef<T> object if T is a class type or an ElementHolder<T> + /// object if T is a C++ primitive data type. + /// The returned value can only be used to read its referenced + /// element. + /// \return The address of the referenced object. + inline pointer operator->() const + { + if (this->directdb_get_) + update_cur_pair(); + return &(curpair_base_); + } + + /// \brief Iterator index operator. + /// + /// If _Off not in a valid range, the returned value will be invalid. + /// Note that you should use a value_type_wrap type to hold the + /// returned value. + /// \param _Off The valid index relative to this iterator. + /// \return Return the element which is at position *this + _Off. + /// The returned value can only be used to read its referenced + /// element. + inline value_type_wrap operator[](difference_type _Off) const + { + self itr(*this + _Off); + return itr.curpair_base_; + } + //@} + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // + // Begin functions that are dbstl specific. + // + /// \name Functions that are dbstl specific. + //@{ + /// \brief Get current index of within the vector. + /// + /// Return the iterators current element's index (0 based). Requires + /// this iterator to be a valid iterator, not end_itr_. + /// \return current index of the iterator. + inline index_type get_current_index() const + { + return pcsr_->get_current_index() - 1; + } + + /// \brief Iterator movement function. + /// + /// Move this iterator to the index "n". If n is not in the valid + /// range, this iterator will be an invalid iterator equal to end() + /// iterator. + /// \param n target element's index. + /// \sa db_vector::end(); + inline void move_to(index_type n) const + { + T d; + int ret; + + this->itr_status_ = pcsr_->move_to(n + 1); + ret = pcsr_->get_current_data(d); + dbstl_assert(ret == 0); + if (this->itr_status_ == 0) + update_cur_pair(); + } + + /// \brief Refresh iterator cached value. + /// \param from_db If not doing direct database get and this parameter + /// is true, we will retrieve data directly from db. + /// \sa db_base_iterator::refresh(bool). + virtual int refresh(bool from_db = true) + { + + if (from_db && !this->directdb_get_) + this->pcsr_->update_current_key_data_from_db( + DbCursorBase::SKIP_NONE); + this->pcsr_->get_current_data(curpair_base_); + + return 0; + } + + /// \brief Close underlying Berkeley DB cursor of this iterator. + /// \sa db_base_iterator::close_cursor() const + inline void close_cursor() const + { + this->pcsr_->close(); + } + + /// \brief Modify bulk buffer size. + /// + /// Bulk read is enabled when creating an + /// iterator, so you later can only modify the bulk buffer size + /// to another value, but can't enable/disable bulk read while an + /// iterator is already alive. + /// \param sz The new size of the bulk read buffer of this iterator. + /// \return Returns true if succeeded, false otherwise. + /// \sa db_base_iterator::set_bulk_buffer(u_int32_t sz) + bool set_bulk_buffer(u_int32_t sz) + { + bool ret = this->pcsr_->set_bulk_buffer(sz); + if (ret) + this->bulk_retrieval_ = sz; + return ret; + } + + /// \brief Get bulk retrieval buffer size in bytes. + /// \return Return current bulk buffer size, or 0 if bulk retrieval is + /// not enabled. + /// \sa db_base_iterator::get_bulk_bufsize() + u_int32_t get_bulk_bufsize() + { + this->bulk_retrieval_ = pcsr_->get_bulk_bufsize(); + return this->bulk_retrieval_; + } + //@} + //////////////////////////////////////////////////////////////////// + +protected: + typedef T value_type_base; + typedef db_base_iterator<T> base; + typedef index_type size_type; + typedef index_type key_type; + typedef T data_type; + + // end_itr_ is always the same, so share it across iterator instances. + static self end_itr_; + + ////////////////// iterator data members ////////////////////////// + // + mutable LazyDupCursor<TRandDbCursor> pcsr_; + + void open() const + { + u_int32_t oflags = 0, oflags2 = 0; + int ret; + DbEnv *penv = this->owner_->get_db_env_handle(); + + oflags2 = this->owner_->get_cursor_open_flags(); + if (!this->read_only_ && penv != NULL) { + BDBOP((penv->get_open_flags(&oflags)), ret); + // Open a writeable cursor when in CDS mode and not + // requesting a read only iterator. + if ((oflags & DB_INIT_CDB) != 0) + oflags2 |= DB_WRITECURSOR; + } + + if (!this->pcsr_) { + + this->pcsr_.set_cursor(new TRandDbCursor( + this->bulk_retrieval_, + this->rmw_csr_, this->directdb_get_)); + } + this->itr_status_ = this->pcsr_->open(this->owner_, oflags2); + } + + // Move the underlying Dbc* cursor to the first element. + // + inline int first() const + { + int ret = 0; + + if ((ret = pcsr_->first()) == 0) + update_cur_pair(); + else + this->itr_status_ = ret; + + return ret; + } + + // Move the underlying Dbc* cursor to the last element. + // + inline index_type last() const + { + index_type pos_index, lsz; + + // last_index() will move the underlying cursor to last record. + lsz = pcsr_->last_index(); + if (lsz > 0) { + pos_index = lsz - 1; + this->itr_status_ = 0; + update_cur_pair(); + } else { + this->itr_status_ = INVALID_ITERATOR_POSITION; + pos_index = INVALID_INDEX; + } + + return pos_index; + } + + void set_curpair_base(const T& d) + { + curpair_base_ = d; + } + + // Update curpair_base_'s data using current underlying key/data pair's + // value. Called on every iterator movement. + // Even if this iterator is invalid, this call is allowed, the + // default value of type T is returned. + // + virtual void update_cur_pair() const + { + this->pcsr_->get_current_data(curpair_base_); + } + + // The 'back' parameter indicates whether to decrease or + // increase the index when moving. The default is to decrease. + // + // Do not throw exceptions here because it is normal to iterate to + // "end()". + // Always move both iterator and cursor synchronously, keep iterators + // data synchronized. + void move_by(const self&itr, difference_type n, bool back = true) const + { + if (n == 0) + return; + if (!back) + n = -n; + if (itr == end_itr_) { + if (n > 0) { // Go back from end() + itr.open(); + itr.last(); + // Moving from end to the last position is + // considered one place. + if (n > 1) { + itr.itr_status_ = + itr.pcsr_->advance(-n + 1); + if (itr.itr_status_ == + INVALID_ITERATOR_POSITION) + itr.inval_pos_type_ = + base::IPT_BEFORE_FIRST; + } + } else + // Can't go further forward from the end. + return; + + } else { + if (n == 1) + itr.itr_status_ = itr.pcsr_->prev(); + else if (n == -1) + itr.itr_status_ = itr.pcsr_->next(); + else + itr.itr_status_ = itr.pcsr_->advance(-n); + } + + itr.update_cur_pair(); + if (itr.itr_status_ != 0) { + if (n > 0) + itr.inval_pos_type_ = base::IPT_BEFORE_FIRST; + else + itr.inval_pos_type_ = base::IPT_AFTER_LAST; + } + } + +private: + // Current data element cached here. + mutable T curpair_base_; + + + friend class db_vector<T, ElementHolder<T> >; + friend class db_vector<T, ElementRef<T> >; + friend class DbCursor<index_type, T>; + friend class RandDbCursor<T>; + friend class ElementRef<T>; + friend class ElementHolder<T>; +}; // db_vector_base_iterator<> + +template <Typename T> + db_vector_base_iterator<T> db_vector_base_iterator<T>::end_itr_; + +/////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////// +// +// db_vector_iterator class template definition +// This class is the iterator class for db_vector, its instances can +// be used to mutate their referenced data element. +// +template <class T, Typename value_type_sub> +class _exported db_vector_iterator : + public db_vector_base_iterator<T> +{ +protected: + typedef db_vector_iterator<T, value_type_sub> self; + typedef db_recno_t index_type; + using db_base_iterator<T>::replace_current_key; +public: + typedef T value_type; + typedef ptrdiff_t difference_type; + typedef difference_type distance_type; + typedef value_type_sub& reference; + + /// This is the return type for operator[]. + /// \sa value_type_wrap operator[](difference_type _Off) const + typedef value_type_sub value_type_wrap; + typedef value_type_sub* pointer; + // Use the STL tag, to ensure compatability with interal STL functions. + // + typedef std::random_access_iterator_tag iterator_category; + + //////////////////////////////////////////////////////////////////// + // Begin public constructors and destructor. + // + /// \name Constructors and destructor + /// Do not construct iterators explictily using these constructors, + /// but call db_vector::begin to get an valid iterator. + /// \sa db_vector::begin + //@{ + db_vector_iterator(const db_vector_iterator<T, value_type_sub>& vi) + : db_vector_base_iterator<T>(vi), + curpair_(vi.curpair_) + { + curpair_._DB_STL_SetIterator(this); + } + + explicit db_vector_iterator(db_container*powner, + u_int32_t b_bulk_retrieval = 0, bool brmw = false, + bool directdbget = true, bool b_read_only = false) + : db_vector_base_iterator<T>(powner, + b_bulk_retrieval, brmw, directdbget, b_read_only) + { + curpair_._DB_STL_SetIterator(this); + this->read_only_ = b_read_only; + this->rmw_csr_ = brmw; + } + + db_vector_iterator() : db_vector_base_iterator<T>() + { + curpair_._DB_STL_SetIterator(this); + } + + db_vector_iterator(const db_vector_base_iterator<T>&obj) + : db_vector_base_iterator<T>(obj) + { + curpair_._DB_STL_CopyData(*obj); + } + + virtual ~db_vector_iterator() + { + this->dead_ = true; + } + //@} + + //////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////////////////////// + // + // Begin functions that shift the iterator position. + // + // These functions are identical to those defined in + // db_vector_base_iterator, but we have to redefine them here because + // the "self" have different definitions. + // + // Do not throw exceptions here because it is normal to iterate to + // "end()". + // Always move both iterator and cursor synchronously, keep iterators + // data synchronized. + // + /// \name Iterator movement operators. + /// These functions have identical behaviors and semantics as those of + /// db_vector_base_iterator, so please refer to equivalent in that + /// class. + //@{ + /// \brief Pre-increment. + /// \return This iterator after incremented. + /// \sa db_vector_base_iterator::operator++() + inline self& operator++() + { + move_by(*this, 1, false); + return *this; + } + + /// \brief Post-increment. + /// \return A new iterator not incremented. + /// \sa db_vector_base_iterator::operator++(int) + inline self operator++(int) + { + self itr(*this); + move_by(*this, 1, false); + + return itr; + } + + /// \brief Pre-decrement. + /// \return This iterator after decremented. + /// \sa db_vector_base_iterator::operator--() + inline self& operator--() + { + move_by(*this, 1, true); + return *this; + } + + /// \brief Post-decrement. + /// \return A new iterator not decremented. + /// \sa db_vector_base_iterator::operator--(int) + inline self operator--(int) + { + self itr = *this; + move_by(*this, 1, true); + return itr; + } + + /// \brief Assignment operator. + /// + /// This iterator will point to the same key/data + /// pair as itr, and have the same configurations as itr. + /// \param itr The right value of the assignment. + /// \return This iterator's reference. + /// \sa db_base_iterator::operator=(const self&) + inline const self& operator=(const self&itr) + { + ASSIGNMENT_PREDCOND(itr) + base::operator=(itr); + + curpair_._DB_STL_CopyData(itr.curpair_); + return itr; + } + + // Always move both iterator and cursor synchronously, keep iterators + // data synchronized. + /// \brief Iterator movement operator. + /// + /// Return another iterator by moving this iterator backward by n + /// elements. + /// \param n The amount and direction of movement. If negative, will + /// move forward by |n| element. + /// \return The new iterator at new position. + /// \sa db_vector_base_iterator::operator+(difference_type n) const + inline self operator+(difference_type n) const + { + self itr(*this); + move_by(itr, n, false); + return itr; + } + + /// \brief Move this iterator backward by n elements. + /// \param n The amount and direction of movement. If negative, will + /// move forward by |n| element. + /// \return Reference to this iterator at new position. + /// \sa db_vector_base_iterator::operator+=(difference_type n) + inline const self& operator+=(difference_type n) + { + move_by(*this, n, false); + return *this; + } + + /// \brief Iterator movement operator. + /// + /// Return another iterator by moving this iterator forward by n + /// elements. + /// \param n The amount and direction of movement. If negative, will + /// move backward by |n| element. + /// \return The new iterator at new position. + /// \sa db_vector_base_iterator::operator-(difference_type n) const + inline self operator-(difference_type n) const + { + self itr(*this); + move_by(itr, n); + + return itr; + } + + /// \brief Move this iterator forward by n elements. + /// \param n The amount and direction of movement. If negative, will + /// move backward by |n| element. + /// \return Reference to this iterator at new position. + /// \sa db_vector_base_iterator::operator-=(difference_type n) + inline const self& operator-=(difference_type n) + { + move_by(*this, n); + return *this; + } + //@} // itr_movement + + /// \brief Iterator distance operator. + /// + /// Return the index difference of this iterator and itr, so if this + /// iterator sits on an element with a smaller index, this call will + /// return a negative number. + /// \param itr The other iterator to substract. itr can be the invalid + /// iterator after last element or before first element, their index + /// will be regarded as last element's index + 1 and -1 respectively. + /// \return The index difference. + /// \sa db_vector_base_iterator::operator-(const self& itr) const + difference_type operator-(const self&itr) const + { + return base::operator-(itr); + } + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // + // Begin functions that retrieve values from the iterator. + // + // Each iterator has a dedicated cursor, and we keep the iterator + // and cursor synchronized all the time. The return value can be used + // to mutate its referenced data element. If directdb_get_ is true(the + // default value), users must call element._DB_STL_SaveElement() to + // store the changes they made to the data element before a next call + // of this function, otherwise the change is lost. + // + /// \name Functions that retrieve values from the iterator. + //@{ + /// \brief Dereference operator. + /// + /// Return the reference to the cached data element, which is an + /// ElementRef<T> object if T is a class type or an ElementHolder<T> + /// object if T is a C++ primitive data type. + /// The returned value can be used to read or update its referenced + /// element. + /// \return The reference to the element this iterator points to. + inline reference operator*() const + { + if (this->directdb_get_) + update_cur_pair(); + return curpair_; // Return reference, no copy construction. + } + + /// \brief Arrow operator. + /// + /// Return the pointer to the cached data element, which is an + /// ElementRef<T> object if T is a class type or an ElementHolder<T> + /// object if T is a C++ primitive data type. + /// The returned value can be used to read or update its referenced + /// element. + /// \return The address of the referenced object. + inline pointer operator->() const + { + if (this->directdb_get_) + update_cur_pair(); + return &curpair_; + } + + // We can't return reference here otherwise we are returning an + // reference to an local object. + /// \brief Iterator index operator. + /// + /// If _Off not in a valid range, the returned value will be invalid. + /// Note that you should use a value_type_wrap type to hold the + /// returned value. + /// \param _Off The valid index relative to this iterator. + /// \return Return the element which is at position *this + _Off, + /// which is an ElementRef<T> object if T is a class type or + /// an ElementHolder<T> object if T is a C++ primitive data type. + /// The returned value can be used to read or update its referenced + /// element. + inline value_type_wrap operator[](difference_type _Off) const + { + self *itr = new self(*this + _Off); + + value_type_sub ref(itr->curpair_); + ref._DB_STL_SetDelItr(); + return ref; + } + //@} // funcs_val + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // + // Begin dbstl specific functions. + // + /// \brief Refresh iterator cached value. + /// \param from_db If not doing direct database get and this parameter + /// is true, we will retrieve data directly from db. + /// \sa db_base_iterator::refresh(bool) + virtual int refresh(bool from_db = true) + { + T d; + + if (from_db && !this->directdb_get_) + this->pcsr_->update_current_key_data_from_db( + DbCursorBase::SKIP_NONE); + this->pcsr_->get_current_data(d); + curpair_._DB_STL_SetData(d); + this->set_curpair_base(d); + return 0; + } + //////////////////////////////////////////////////////////////////// + +protected: + typedef T value_type_base; + typedef db_vector_base_iterator<T> base; + typedef index_type size_type; + typedef index_type key_type; + typedef T data_type; + typedef db_vector<T, value_type_sub> OwnerType; + + friend class db_vector<T, value_type_sub>; + friend class DbCursor<index_type, T>; + friend class RandDbCursor<T>; + friend class ElementRef<T>; + friend class ElementHolder<T>; + + // The data item this iterator currently points to. It is updated + // on every iterator movement. By default directdb_get_ member is true + // (can be set to false via container's begin() method), so whenever + // operator */-> is called, it is retrieved from db, to support + // concurrency. + mutable value_type_sub curpair_; + + virtual void delete_me() const + { + if (!this->dead_) + delete this; + } + + virtual self* dup_itr() const + { + self *itr = new self(*this); + // The curpair_ of itr does not delete itr, the independent + // one does. + // itr->curpair_._DB_STL_SetDelItr(); + return itr; + } + + // Replace the current key/data pair's data with d. Can only be called + // by non-const iterator. Normally internal functions do not wrap + // transactions, but replace_current is used in assignment by the user + // so it needs to be wrapped. + virtual int replace_current(const T& d) + { + int ret = 0; + + if (this->read_only_) { + THROW(InvalidFunctionCall, ( +"replace_current can't be called via a readonly iterator.")); + } + ret = this->pcsr_->replace(d); + + return ret; + } + + // This function is part of the db_base_iterator interface, but + // is not valid for db_vector_iterator. + virtual int replace_current_key(const T&) { + THROW(InvalidFunctionCall, ( +"replace_current_key not supported by db_vector_iterator<>")); + } + + // Update curpair_'s data using current underlying key/data pair's + // value. Called on every iterator movement. + // Even if this iterator is invalid, this call is allowed, the + // default value of type T is returned. + // + virtual void update_cur_pair() const + { + T t; + + this->pcsr_->get_current_data(t); + curpair_._DB_STL_CopyData(t); + base::update_cur_pair(); + } + + +}; // db_vector_iterator +//@} // db_vector_iterators +//@} // dbstl_iterators + +// These operators make "n + itr" expressions valid. Without it, you can only +// use "itr + n" +template <Typename T> +db_vector_base_iterator<T> operator+(typename db_vector_base_iterator<T>:: + difference_type n, db_vector_base_iterator<T> itr) +{ + db_vector_base_iterator<T> i2 = itr; + + i2 += n; + return i2; +} + +template <Typename T, Typename value_type_sub> +db_vector_iterator<T, value_type_sub> operator+( + typename db_vector_iterator<T, value_type_sub>:: + difference_type n, db_vector_iterator<T, value_type_sub> itr) +{ + db_vector_iterator<T, value_type_sub> i2 = itr; + + i2 += n; + return i2; +} + +template <Typename iterator, Typename iterator2> +db_reverse_iterator<iterator, iterator2> operator+(typename + db_reverse_iterator<iterator, iterator2>::difference_type n, + db_reverse_iterator<iterator, iterator2> itr) +{ + db_reverse_iterator<iterator, iterator2> i2 = itr; + // The db_reverse_iterator<iterator>::operator+ will substract + // n in it, we pass the + here. + // + i2 += n; + return i2; +} + +/// \ingroup dbstl_containers +//@{ +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// +// db_vector class template definition +// +/// The db_vector class has the union set of public member functions as +/// std::vector, std::deque and std::list, and each method has identical +/// default semantics to that in the std equivalent containers. +/// The difference is that the data is maintained using a Berkeley DB database +/// as well as some Berkeley DB related extensions. +/// \param T The type of data to store. +/// \param value_type_sub If T is a class/struct type, do not specify anything +/// for this parameter; Otherwise, specify ElementHolder<T> to it. +/// Database(dbp) and environment(penv) handle requirement(applies for all +/// constructors of this class template): +/// dbp must meet the following requirement: +/// 1. dbp must be a DB_RECNO type of database handle. +/// 2. DB_THREAD must be set to dbp's open flags. +/// 3. An optional flag DB_RENUMBER is required if the container object +/// is supposed to be a std::vector or std::deque equivalent; Not +/// required if it is a std::list equivalent. But dbstl will not check +/// whether DB_RENUMBER is set to this database handle. +/// Setting DB_RENUMBER will cause the index values of all elements in +/// the underlying databse to be maintained consecutive and in order, +/// which involves potentially a lot of work because many indices may +/// be updated. +/// See the db_container(Db*, DbEnv*) for more information about the two +/// parameters. +/// \sa db_container db_container(Db*, DbEnv*) db_container(const db_container&) +// +template <Typename T, Typename value_type_sub> +class _exported db_vector: public db_container +{ +private: + typedef db_vector<T, value_type_sub> self; + typedef db_recno_t index_type; +public: + typedef T value_type; + typedef value_type_sub value_type_wrap; + typedef value_type_sub* pointer; + typedef db_vector_iterator<T, value_type_sub> iterator; + typedef db_vector_base_iterator<T> const_iterator; + typedef index_type size_type; + typedef db_reverse_iterator<iterator, const_iterator> reverse_iterator; + typedef const value_type_sub* const_pointer; + typedef db_reverse_iterator<const_iterator, iterator> + const_reverse_iterator; + + typedef typename value_type_sub::content_type const_value_type; + + // We don't use value_type_sub& as a reference type here because + // we created a local iterator object in operator[], and we must keep + // an active cursor on the key/data pair. Thus operator[] needs to + // return an object rather than a reference to a local object. + typedef value_type_sub reference; + + // We can't return reference here because we are using a local iterator. + typedef const_value_type const_reference; + typedef ptrdiff_t difference_type; + + ///////////////////////////////////////////////////////////////////// + // Begin functions that create iterators. + /// \name Iterator functions. + //@{ + /// \brief Create a read-write or read-only iterator. + /// + /// We allow users to create a readonly + /// iterator here so that they don't have to use a const container + /// to create a const_iterator. But using const_iterator + /// is faster. The flags set via db_container::set_cursor_oflags() is + /// used as the cursor open flags. + /// \param rmw Whether this iterator will open a Berkeley DB + /// cursor with DB_RMW flag set. If the iterator is used to read a + /// key/data pair, then update it and store back to db, it is good + /// to set the DB_RMW flag, by specifying RMWItrOpt::read_modify_write() + /// If you don't want to set the DB_RMW flag, specify + /// RMWItrOpt::no_read_modify_write(), which is the default behavior. + /// \param readonly Whether the iterator is created as a readonly + /// iterator. Read only iterators can not update its underlying + /// key/data pair. + /// \param bulk_read Whether read database key/data pairs in bulk, by + /// specifying DB_MULTIPLE_KEY flag to underlying cursor's Dbc::get + /// function. Only readonly iterators can do bulk retrieval, if + /// iterator is not read only, this parameter is ignored. Bulk + /// retrieval can accelerate reading speed because each database read + /// operation will read many key/data pairs, thus saved many database + /// read operations. The default bulk buffer size is 32KB, you can + /// set your desired bulk buffer size by specifying + /// BulkRetrievalOpt::bulk_retrieval(your_bulk_buffer_size); + /// If you don't want bulk retrieval, set + /// BulkRetrievalItrOpt::no_bulk_retrieval() as the real parameter. + /// \param directdb_get Whether always read key/data pair from backing + /// db rather than using the value cached in the iterator. The current + /// key/data pair is cached in the iterator and always kept updated on + /// iterator movement, but in some extreme conditions, errors can + /// happen if you use cached key/data pairs without always refreshing + /// them from database. By default we are always reading from database + /// when we are accessing the data the iterator sits on, except when + /// we are doing bulk retrievals. But your application can gain extra + /// performance promotion if you can set this flag to false. + /// \return The created iterator. + /// \sa db_container::set_cursor_oflags(); + iterator begin(ReadModifyWriteOption rmw = + ReadModifyWriteOption::no_read_modify_write(), + bool readonly = false, BulkRetrievalOption bulk_read = + BulkRetrievalOption::no_bulk_retrieval(), + bool directdb_get = true) + { + bool b_rmw; + u_int32_t bulk_retrieval; + + bulk_retrieval = 0; + b_rmw = (rmw == ReadModifyWriteOption::read_modify_write()); + if (readonly && b_rmw) + b_rmw = false; + // Bulk only available to readonly iterators. + if (readonly && bulk_read == + BulkRetrievalOption::BulkRetrieval) + bulk_retrieval = bulk_read.bulk_buf_size(); + + iterator itr((db_container*)this, bulk_retrieval, b_rmw, + directdb_get, readonly); + + open_itr(itr, readonly); + itr.first(); + + return itr; + } + + /// \brief Create a const iterator. + /// + /// The created iterator can only be used to read its referenced + /// data element. Can only be called when using a const reference to + /// the contaienr object. The parameters have identical meanings and + /// usage to those of the other non-const begin function. + /// \param bulkretrieval Same as that of + /// begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \param directdb_get Same as that of + /// begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \return The created const iterator. + /// \sa begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + const_iterator begin(BulkRetrievalOption bulkretrieval = + (BulkRetrievalOption::no_bulk_retrieval()), + bool directdb_get = true) const + { + u_int32_t b_bulk_retrieval = (bulkretrieval == + BulkRetrievalOption::BulkRetrieval) ? + bulkretrieval.bulk_buf_size() : 0; + + const_iterator itr((db_container*)this, b_bulk_retrieval, + false, directdb_get, true); + + open_itr(itr, true); + itr.first(); + + return itr; + } + + /// \brief Create an open boundary iterator. + /// \return Returns an invalid iterator denoting the position after + /// the last valid element of the container. + inline iterator end() + { + end_itr_.owner_ = (db_container*)this; + end_itr_.inval_pos_type_ = db_base_iterator<T>::IPT_AFTER_LAST; + return end_itr_; + } + + /// \brief Create an open boundary iterator. + /// \return Returns an invalid const iterator denoting the position + /// after the last valid element of the container. + inline const_iterator end() const + { + end_itr_.owner_ = (db_container*)this; + end_itr_.inval_pos_type_ = db_base_iterator<T>::IPT_AFTER_LAST; + return end_itr_; + } + + /// \brief Create a reverse iterator. + /// + /// This function creates a reverse iterator initialized to sit on the + /// last element in the underlying database, and can be used to + /// read/write. The meaning and usage of + /// its parameters are identical to the above begin function. + /// \param rmw Same as that of + /// begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \param bulk_read Same as that of + /// begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \param directdb_get Same as that of + /// begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \param readonly Same as that of + /// begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \return The created iterator. + /// \sa begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + reverse_iterator rbegin( + ReadModifyWriteOption rmw = + ReadModifyWriteOption::no_read_modify_write(), + bool readonly = false, BulkRetrievalOption bulk_read = + BulkRetrievalOption::no_bulk_retrieval(), + bool directdb_get = true) + { + u_int32_t bulk_retrieval = 0; + + reverse_iterator itr(end()); + itr.rmw_csr_ = + (rmw == (ReadModifyWriteOption::read_modify_write())); + itr.directdb_get_ = directdb_get; + itr.read_only_ = readonly; + itr.owner_ = (db_container*)this; + + // Bulk only available to readonly iterators. + if (readonly && + bulk_read == BulkRetrievalOption::BulkRetrieval) + bulk_retrieval = bulk_read.bulk_buf_size(); + itr.bulk_retrieval_ = bulk_retrieval; + + return itr; + } + + /// \brief Create a const reverse iterator. + /// + /// This function creates a const reverse iterator initialized to sit + /// on the last element in the backing database, and can only read the + /// element, it is only available to const db_vector containers. + /// The meaning and usage of its parameters are identical as above. + /// \param bulkretrieval Same as that of + /// begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \param directdb_get Same as that of + /// begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \return The created iterator. + /// \sa begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + const_reverse_iterator rbegin(BulkRetrievalOption bulkretrieval = + BulkRetrievalOption(BulkRetrievalOption::no_bulk_retrieval()), + bool directdb_get = true) const + { + const_reverse_iterator itr(end()); + itr.bulk_retrieval_ = + (bulkretrieval == (BulkRetrievalOption::BulkRetrieval) ? + bulkretrieval.bulk_buf_size() : 0); + + itr.directdb_get_ = directdb_get; + itr.read_only_ = true; + itr.owner_ = (db_container*)this; + + return itr; + } + + /// \brief Create an open boundary iterator. + /// \return Returns an invalid iterator denoting the position + /// before the first valid element of the container. + inline reverse_iterator rend() + { + reverse_iterator itr; // No cursor created. + + itr.itr_status_ = INVALID_ITERATOR_POSITION; + itr.owner_ = (db_container*)this; + itr.inval_pos_type_ = db_base_iterator<T>::IPT_BEFORE_FIRST; + return itr; + } + + /// \brief Create an open boundary iterator. + /// \return Returns an invalid const iterator denoting the position + /// before the first valid element of the container. + inline const_reverse_iterator rend() const + { + const_reverse_iterator itr; // No cursor created. + + itr.itr_status_ = INVALID_ITERATOR_POSITION; + itr.owner_ = (db_container*)this; + itr.inval_pos_type_ = db_base_iterator<T>::IPT_BEFORE_FIRST; + return itr; + } + //@} // iterator_funcs + ///////////////////////////////////////////////////////////////////// + + /// \brief Get container size. + /// This function supports auto-commit. + // All container methods using internal working iterators can be used + // to implement autocommit if no DB operations can be directly used, + // because the work iterator is not supposed to point to a specific + // record before reopening it. + /// \return Return the number of elements in this container. + /// \sa http://www.cplusplus.com/reference/stl/vector/size.html + size_type size() const + { + index_type sz; + + try { + this->begin_txn(); + const_iterator derefitr; + init_itr(derefitr); + open_itr(derefitr, true); + sz = derefitr.last() + 1; + this->commit_txn(); + return (size_type)sz; // Largest index is the size. + } catch (...) { + this->abort_txn(); + throw; + } + } + + /// \name Huge return + /// These two functions return 2^30, denoting a huge number that does + /// not overflow, because dbstl does not have to manage memory space. + /// But the return value is not the real limit, see the Berkeley DB + /// database limits for the limits. + //@{ + /// Get max size. + /// The returned size is not the actual limit of database. See the + /// Berkeley DB limits to get real max size. + /// \return A meaningless huge number. + inline size_type max_size() const + { + return SIZE_T_MAX; + } + + /// Get capacity. + inline size_type capacity() const + { + return SIZE_T_MAX; + } + //@} + + /// Returns whether this container is empty. + /// \return True if empty, false otherwise. + inline bool empty() const + { + const_iterator itr = begin(); + return itr.itr_status_ == INVALID_ITERATOR_POSITION; + } + + ///////////////////////////////////////////////////////////////////// + // Begin element access functions. + // + /// \name Element access functions. + /// The operator[] and at() only come from std::vector and std::deque, + /// If you are using db_vector as std::list, you don't have + /// to set DB_RENUMBER flag to the backing database handle, and you get + /// better performance, but at the same time you can't use these + /// functions. Otherwise if you have set the DB_RENUMBER flag to the + /// backing database handle, you can use this function though it is an + /// std::list equivalent. + //@{ + /// Index operator, can act as both a left value and a right value. + /// \param n The valid index of the vector. + /// \return The reference to the element at specified position. + inline reference operator[](index_type n) + { + iterator derefitr, *pitr; + + init_itr(derefitr); + open_itr(derefitr); + if (n == INVALID_INDEX) + n = derefitr.last(); + derefitr.move_to(n); + + pitr = new iterator(derefitr); + reference ref(pitr->curpair_); + ref._DB_STL_SetDelItr(); + return ref; + } + + /// \brief Read only index operator. + /// + /// Only used as a right value, no need for assignment capability. + /// The return value can't be used to update the element. + /// \param n The valid index of the vector. + /// \return The const reference to the element at specified position. + inline const_reference operator[](index_type n) const + { + const_iterator derefitr; + + init_itr(derefitr); + open_itr(derefitr); + if (n == INVALID_INDEX) + n = derefitr.last(); + derefitr.move_to(n); + + // _DB_STL_value returns a reference + return (*derefitr); + } + + /// \brief Index function. + /// \param n The valid index of the vector. + /// \return The reference to the element at specified position, can + /// act as both a left value and a right value. + /// \sa http://www.cplusplus.com/reference/stl/vector/at.html + inline reference at(index_type n) + { + return (*this)[n]; + } + + /// \brief Read only index function. + /// + /// Only used as a right value, no need for assignment capability. + /// The return value can't be used to update the element. + /// \param n The valid index of the vector. + /// \return The const reference to the element at specified position. + /// \sa http://www.cplusplus.com/reference/stl/vector/at.html + inline const_reference at(index_type n) const + { + return (*this)[n]; + } + + /// Return a reference to the first element. + /// \return Return a reference to the first element. + /// \sa http://www.cplusplus.com/reference/stl/vector/front.html + inline reference front() + { + iterator itr, *pitr; + + init_itr(itr); + open_itr(itr); + itr.first(); + + pitr = new iterator(itr); + reference ref(pitr->curpair_); + ref._DB_STL_SetDelItr(); + return ref; + } + + /// \brief Return a const reference to the first element. + /// + /// The return value can't be used to update the element. + /// \return Return a const reference to the first element. + /// \sa http://www.cplusplus.com/reference/stl/vector/front.html + inline const_reference front() const + { + const_iterator itr; + + init_itr(itr); + open_itr(itr); + itr.first(); + return (*itr); + } + + /// Return a reference to the last element. + /// \return Return a reference to the last element. + /// \sa http://www.cplusplus.com/reference/stl/vector/back.html + inline reference back() + { + iterator itr, *pitr; + + init_itr(itr); + open_itr(itr); + itr.last(); + + pitr = new iterator(itr); + reference ref(pitr->curpair_); + ref._DB_STL_SetDelItr(); + return ref; + } + + /// \brief Return a reference to the last element. + /// + /// The return value can't be used to update the element. + /// \return Return a reference to the last element. + /// \sa http://www.cplusplus.com/reference/stl/vector/back.html + inline const_reference back() const + { + const_iterator itr; + + init_itr(itr); + open_itr(itr); + itr.last(); + return (*itr); + } + //@} + + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // + // Begin db_vector constructors and destructor. + /// \brief Constructor. + /// + /// Note that we do not need an allocator in db-stl containser, but + /// we need backing up Db* and DbEnv*, and we have to verify that the + /// passed in bdb handles are valid for use by the container class. + /// See class detail for handle requirement. + /// \param dbp The same as that of db_container(Db*, DbEnv*); + /// \param penv The same as that of db_container(Db*, DbEnv*); + /// \sa db_container(Db*, DbEnv*); + // + explicit db_vector(Db* dbp = NULL, DbEnv *penv = NULL) + : base(dbp, penv) + { + const char *errmsg; + + this->open_db_handles(dbp, penv, DB_RECNO, DB_CREATE | + DB_THREAD, DB_RENUMBER); + if ((errmsg = verify_config(dbp, penv)) != NULL) { + THROW(InvalidArgumentException, ("Db*", errmsg)); + } + this->set_db_handle_int(dbp, penv); + } + + /// \brief Constructor. + /// + /// This function supports auto-commit. + /// Insert n elements of T type into the database, the value of the + /// elements is the default value or user set value. + /// See class detail for handle requirement. + /// \param n The number of elements to insert. + /// \param val The value of elements to insert. + /// \param dbp The same as that of db_container(Db*, DbEnv*); + /// \param penv The same as that of db_container(Db*, DbEnv*); + /// \sa db_vector(Db*, DbEnv*); db_container(Db*, DbEnv*); + explicit db_vector(size_type n, const T& val = T(), Db* dbp = NULL, + DbEnv *penv = NULL) : base(dbp, penv) + { + size_type i; + const char *errmsg; + + this->open_db_handles(dbp, penv, DB_RECNO, DB_CREATE | + DB_THREAD, DB_RENUMBER); + if ((errmsg = verify_config(dbp, penv)) != NULL) { + THROW(InvalidArgumentException, ("Db*", errmsg)); + } + this->set_db_handle_int(dbp, penv); + + this->begin_txn(); + // This transaction will prevent push_back to autocommit, + // as expected, because a single push_back should not be + // automatic in this function + // + try { + for (i = 0; i < n; i++) + push_back(val); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + } + + /// \brief Copy constructor. + /// + /// This function supports auto-commit. + /// Insert all elements in x into this container. + /// \sa db_container(const db_container&) + db_vector(const self& x) : db_container(x) + { + // This objects underlying db should have been opened already, + // only copy db contents. + // + verify_db_handles(x); + this->set_db_handle_int(this->clone_db_config( + x.get_db_handle()), x.get_db_env_handle()); + + this->begin_txn(); + + try { + copydb(x); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + } + + /// Range constructor. + /// This function supports auto-commit. + // The order of parameters has to be altered in order to avoid + // clashing with the other constuctor above (the one with size_type + // as first parameter). + /// Insert a range of elements into this container. The range is + /// [first, last), which contains elements that can be converted to + /// type T automatically. + /// See class detail for handle requirement. + /// \param dbp The same as that of db_container(Db*, DbEnv*); + /// \param penv The same as that of db_container(Db*, DbEnv*); + /// \param first Range closed boundary. + /// \param last Range open boundary. + /// \sa db_vector(Db*, DbEnv*); + template <class InputIterator> + db_vector(Db*dbp, DbEnv *penv, + InputIterator first, InputIterator last) : base(dbp, penv) + { + const char *errmsg; + + this->open_db_handles(dbp, penv, DB_RECNO, DB_CREATE | + DB_THREAD, DB_RENUMBER); + if ((errmsg = verify_config(dbp, penv)) != NULL) + THROW(InvalidArgumentException, ("Db*", errmsg)); + + this->set_db_handle_int(dbp, penv); + + this->begin_txn(); + try { + push_range(first, last); + } catch (...) { + this->abort_txn(); + throw; + } + + this->commit_txn(); + } + + /// Range constructor. + /// This function supports auto-commit. + /// Insert the range of elements in [first, last) into this container. + /// See class detail for handle requirement. + /// \param dbp The same as that of db_container(Db*, DbEnv*); + /// \param penv The same as that of db_container(Db*, DbEnv*); + /// \param first Range closed boundary. + /// \param last Range open boundary. + /// \sa db_vector(Db*, DbEnv*); + db_vector(const_iterator first, const_iterator last, + Db*dbp = NULL, DbEnv *penv = NULL) : base(dbp, penv) + { + const char *errmsg; + + this->open_db_handles(dbp, penv, DB_RECNO, DB_CREATE | + DB_THREAD, DB_RENUMBER); + if ((errmsg = verify_config(dbp, penv)) != NULL) + THROW(InvalidArgumentException, ("Db*", errmsg)); + this->set_db_handle_int(dbp, penv); + + this->begin_txn(); + try { + push_range(first, last); + } catch (...) { + this->abort_txn(); + throw; + } + + this->commit_txn(); + } + + // We don't have to close Berkeley DB database or environment handles + // because all related handles are managed by ResourceManager and + // closed in the right order when the program exits. + // + virtual ~db_vector() { + } + + //////////////////////////////////////////////////////////////////// + + /// \brief Container assignment operator. + /// + /// This function supports auto-commit. + /// This db_vector is assumed to be valid for use, only copy + /// content of x into this container. + /// \param x The right value container. + /// \return The container x's reference. + const self& operator=(const self& x) + { + ASSIGNMENT_PREDCOND(x) + // TODO: rename verify_db_handles to validate_db_handle + db_container::operator =(x); + verify_db_handles(x); + this->begin_txn(); + try { + copydb(x); + } catch (...) { + this->abort_txn(); + throw; + } + + this->commit_txn(); + return x; + } + + //////////////////////////////////////////////////////////////////// + // + // Begin db_vector comparison functions. + /// \name Compare functions. + /// \sa http://www.sgi.com/tech/stl/Vector.html + //@{ + /// \brief Container equality comparison operator. + /// + /// This function supports auto-commit. + /// \return Compare two vectors, return true if they have identical + /// sequences of elements, otherwise return false. + /// \param v2 The vector to compare against. + template <Typename T2, Typename T3> + bool operator==(const db_vector<T2, T3>& v2) const + { + bool ret; + size_t sz; + + verify_db_handles(v2); + typename self::iterator i1; + typename db_vector<T2, T3>::iterator i2; + + try { + this->begin_txn(); + if ((sz = this->size()) != v2.size()) { + ret = false; + this->commit_txn(); + return ret; + } + if (sz == 0) { + ret = true; + this->commit_txn(); + return ret; + } + + // Identical size, compare each element. + for (i1 = begin(), i2 = v2.begin(); i1 != end() && + i2 != v2.end(); ++i1, ++i2) + if (!((T)(*i1) == (T2)(*i2))) { + ret = false; + this->commit_txn(); + return ret; + } + + // All elements equal, the two vectors are equal. + ret = true; + this->commit_txn(); + return ret; + } catch (...) { + this->abort_txn(); + throw; + } + } + + /// \brief Container in-equality comparison operator. + /// + /// This function supports auto-commit. + /// \param v2 The vector to compare against. + /// \return Returns false if elements in each slot of both + /// containers equal; Returns true otherwise. + template <Typename T2, Typename T3> + bool operator!=(const db_vector<T2, T3>& v2) const + { + return !this->operator==(v2); + } + + /// \brief Container equality comparison operator. + /// + /// This function supports auto-commit. + /// \return Compare two vectors, return true if they have identical + /// elements, otherwise return false. + bool operator==(const self& v2) const + { + bool ret; + + COMPARE_CHECK(v2) + verify_db_handles(v2); + + try { + this->begin_txn(); + if (this->size() != v2.size()) { + ret = false; + this->commit_txn(); + return ret; + } + typename self::const_iterator i1; + typename self::const_iterator i2; + // Identical size, compare each element. + for (i1 = begin(), i2 = v2.begin(); i1 != end() && + i2 != v2.end(); ++i1, ++i2) + if (!(*i1 == *i2)) { + ret = false; + this->commit_txn(); + return ret; + } + + // All elements are equal, the two vectors are equal. + ret = true; + this->commit_txn(); + return ret; + } catch (...) { + this->abort_txn(); + throw; + } + } + + /// \brief Container in-equality comparison operator. + /// + /// This function supports auto-commit. + /// \param v2 The vector to compare against. + /// \return Returns false if elements in each slot of both + /// containers equal; Returns true otherwise. + bool operator!=(const self& v2) const + { + return !this->operator==(v2); + } + + /// \brief Container less than comparison operator. + /// + /// This function supports auto-commit. + /// \param v2 The container to compare against. + /// \return Compare two vectors, return true if this is less + /// than v2, otherwise return false. + bool operator<(const self& v2) const + { + bool ret; + + if (this == &v2) + return false; + + verify_db_handles(v2); + typename self::const_iterator i1; + typename self::const_iterator i2; + size_t s1, s2, sz, i; + + try { + this->begin_txn(); + s1 = this->size(); + s2 = v2.size(); + sz = s1 < s2 ? s1 : s2; + + // Compare each element. + for (i1 = begin(), i = 0, i2 = v2.begin(); + i < sz; + ++i1, ++i2, ++i) { + if (*i1 == *i2) + continue; + else { + if (*i1 < *i2) + ret = true; + else + ret = false; + this->commit_txn(); + return ret; + } + } + + ret = s1 < s2; + this->commit_txn(); + return ret; + } catch (...) { + this->abort_txn(); + throw; + } + } + //@} // cmp_funcs + //////////////////////////////////////////////////////////////////// + + /// \brief Resize this container to specified size n, insert values t + /// if need to enlarge the container. + /// + /// This function supports auto-commit. + /// \param n The number of elements in this container after the call. + /// \param t The value to insert when enlarging the container. + /// \sa http://www.cplusplus.com/reference/stl/vector/resize.html + inline void resize(size_type n, T t = T()) + { + size_t i, sz; + + try { + begin_txn(); + if (n == (sz = size())) { + commit_txn(); + return; + } + + if (n < sz) // Remove sz - n elements at tail. + erase(begin() + n, end()); + else + for (i = sz; i < n; i++) + push_back(t); + commit_txn(); + } catch (...) { + abort_txn(); + throw; + } + } + + /// \brief Reserve space. + /// + /// The vector is backed by Berkeley DB, we always have enough space. + /// This function does nothing, because dbstl does not have to manage + /// memory space. + inline void reserve(size_type /* n */) + { + } + + /** \name Assign functions + See the function documentation for the correct usage of b_truncate + parameter. + @{ + The following four member functions have default parameter b_truncate, + because they require all key/data pairs in the database be deleted + before the real operation, and by default we use Db::truncate to + truncate the database rather than delete the key/data pairs one by + one, but Db::truncate requirs no open cursors on the database handle, + and the four member functions will close any open cursors of backing + database handle in current thread, but can do nothing to cursors of + other threads opened from the same database handle. + So you must make sure there are no open cursors of the database handle + in any other threads. On the other hand, users can specify "false" to + the b_truncate parameter and thus the key/data pairs will be deleted + one by one. Other than that, they have identical behaviors as their + counterparts in std::vector. + \sa http://www.cplusplus.com/reference/stl/vector/assign.html + */ + /// Assign a range [first, last) to this container. + /// \param first The range closed boundary. + /// \param last The range open boundary. + /// \param b_truncate See its member group doc for details. + template <class InputIterator> + void assign ( InputIterator first, InputIterator last, + bool b_truncate = true) + { + if (this->get_db_handle() == NULL) + return; + + this->begin_txn(); + try { + clear(b_truncate); + push_range(first, last); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + } + + /// Assign a range [first, last) to this container. + /// \param first The range closed boundary. + /// \param last The range open boundary. + /// \param b_truncate See its member group doc for details. + void assign(const_iterator first, const_iterator last, + bool b_truncate = true) + { + if (this->get_db_handle() == NULL) + return; + + this->begin_txn(); + try { + clear(b_truncate); + push_range(first, last); + + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + } + + /// Assign n number of elements of value u into this container. + /// \param n The number of elements in this container after the call. + /// \param u The value of elements to insert. + /// \param b_truncate See its member group doc for details. + /// This function supports auto-commit. + void assign ( size_type n, const T& u, bool b_truncate = true) + { + if (this->get_db_handle() == NULL) + return; + + this->begin_txn(); + try { + clear(b_truncate); + size_t i; + for (i = 0; i < n; i++) + push_back(u); + } catch (...) { + this->abort_txn(); + throw; + } + + this->commit_txn(); + } + //@} // assign_funcs + + // Directly use DB->put, so that when there is no explicit transaction, + // it is autocommit. This + // method is often called by other db_vector methods, in that case + // those methods will begin/commit_txn internally, causing push_back + // to not autocommit, as expected. + /// \brief Push back an element into the vector. + /// + /// This function supports auto-commit. + /// \param x The value of element to push into this vector. + /// \sa http://www.cplusplus.com/reference/stl/vector/push_back.html + inline void push_back ( const T& x ) + { + index_type k0 = 0; // This value is ignored. + int ret; + + // x may be an temporary object, so must copy it. + DataItem dt(x, false), k(k0, true); + // In CDS mode, the current transaction is the DB_TXN created + // by cds_group_begin. + BDBOP(this->get_db_handle()->put(ResourceManager::instance()-> + current_txn(this->get_db_env_handle()), + &(k.get_dbt()), &(dt.get_dbt()), DB_APPEND), ret); + } + + /// \brief Pop out last element from the vector. + /// + /// This function supports auto-commit. + /// \sa http://www.cplusplus.com/reference/stl/vector/pop_back.html + void pop_back () + { + try { + iterator derefitr; + + this->begin_txn(); + init_itr(derefitr); + open_itr(derefitr); + derefitr.last(); + derefitr.pcsr_->del(); + this->commit_txn(); + } catch(...) { + this->abort_txn(); + throw; + } + } + + //////////////////////////////////////////////////////////////////// + // + // Begin std::deque and std::list specific public functions. + // + // These methods are not in std::vector, but are in std::list and + // std::deque. They are defined here so that db_vector can be used to + // replace std::list and std::deque. + /// \name Functions specific to deque and list + /// These functions come from std::list and std::deque, and have + /// identical behaviors to their counterparts in std::list/std::deque. + /// \sa http://www.cplusplus.com/reference/stl/deque/pop_front.html + /// http://www.cplusplus.com/reference/stl/deque/push_front.html + //@{ + /// \brief Push an element x into the vector from front. + /// \param x The element to push into this vector. + /// This function supports auto-commit. + void push_front (const T& x) + { + int flag, ret; + + try { + this->begin_txn(); + iterator derefitr; + init_itr(derefitr); + open_itr(derefitr); + // MOVE iterator and cursor to 1st element. + ret = derefitr.first(); + if (ret < 0) + flag = DB_KEYLAST; // empty + else + flag = DB_BEFORE; + derefitr.pcsr_->insert(x, flag); + this->commit_txn(); + } catch(...) { + this->abort_txn(); + throw; + } + } + + /// \brief Pop out the front element from the vector. + /// + /// This function supports auto-commit. + void pop_front () + { + try { + this->begin_txn(); + iterator derefitr; + init_itr(derefitr); + open_itr(derefitr); + derefitr.first(); + derefitr.pcsr_->del(); + this->commit_txn(); + } catch(...) { + this->abort_txn(); + throw; + } + + } + //@} + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // + // Begin insert and erase functions. + // + // This method can not be autocommit because pos can not be reopened + // while it already points to a dest position. In order to gain + // transaction it should have already been opened in a transactional + // context, so it is meaningless to wrap the insert operation with + // begin/commit transaction in this method. + /// \name Insert functions + /// The iterator pos in the functions must be a read-write iterator, + /// can't be read only. + /// \sa http://www.cplusplus.com/reference/stl/vector/insert.html + //@{ + /// \brief Insert x before position pos. + /// \param x The element to insert. + /// \param pos The position before which to insert. + iterator insert (iterator pos, const T& x) + { + u_int32_t flag; + bool isempty; + + make_insert_args(pos, flag, isempty); + + pos.pcsr_->insert(x, flag); + pos.update_cur_pair(); // Sync with underlying cursor. + + return pos; // Returns the new position's iterator. + } + + /// \brief Insert n number of elements x before position pos. + /// \param x The element to insert. + /// \param pos The position before which to insert. + /// \param n The number of elements to insert. + void insert (iterator pos, size_type n, const T& x) + { + u_int32_t flag; + size_t i; + bool isempty; + + make_insert_args(pos, flag, isempty); + + for (i = 0; i < n; i++) { + pos.pcsr_->insert(x, flag); + pos.update_cur_pair(); + // Move the underlying Dbc*cursor to next record + // (i.e. the orig record it pointed to before + // the insertion). So it will point to + // the new record after insertion. + // + if (flag == DB_BEFORE) + ++pos; + + // If using DB_AFTER flag, no need to move because + // cursor already points to the newly inserted record + // after the orig record it pointed to. + // + + // There is already data in the underlying database + // so use DB_BEFORE unless pos is begin() and the + // vector was empty before this insert call. + // + if (flag == DB_KEYLAST) { + if(isempty) + flag = DB_AFTER; + else + // This branch can never be reached because any + // iterator of a empty container can only have its + // cursor at the begin() position. + // + flag = DB_BEFORE; + } + } + } + + /// \brief Range insertion. + /// + /// Insert elements in range [first, last) into this vector before + /// position pos. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + /// \param pos The position before which to insert. + template <class InputIterator> + void insert (iterator pos, InputIterator first, InputIterator last) + { + u_int32_t flag; + InputIterator itr; + bool isempty; + + make_insert_args(pos, flag, isempty); + // !!!XXX + // The cursor will point to the newly inserted record, so we + // need to move the cursor to the original one. + // + + for (itr = first; itr != last; ++itr) { + pos.pcsr_->insert(*itr, flag); + pos.update_cur_pair(); + // Move the underlying Dbc*cursor to next record + // (i.e. the orig record it pointed to before + // the insertion). So it will point to + // the new record after insertion. + // + if (flag == DB_BEFORE) + ++pos; + // There is already data in the underlying database + // so use DB_BEFORE unless pos is begin() and the + // vector was empty before this insert call. + // + if (flag == DB_KEYLAST) { + // pos == begin() && this was empty + if(isempty) + flag = DB_AFTER; + else + // This branch can never be reached because any + // iterator of a empty container can only have its + // cursor at the begin() position. + // + flag = DB_BEFORE; + } + } + } + + // this method can not be autocommitted, reason as above + /// \brief Range insertion. + /// + /// Insert elements in range [first, last) into this vector before + /// position pos. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + /// \param pos The position before which to insert. + void insert (iterator pos, const_iterator first, const_iterator last) + { + u_int32_t flag; + iterator itr; + bool isempty; + + make_insert_args(pos, flag, isempty); + + for (itr = first; itr != last; ++itr) { + pos.pcsr_->insert(*itr, flag); + pos.update_cur_pair(); + // Move the underlying Dbc*cursor to next record + // (i.e. the orig record it pointed to before + // the insertion). So it will point to + // the new record after insertion. + // + if (flag == DB_BEFORE) + ++pos; + // There is already data in the underlying database + // so use DB_BEFORE unless pos is begin() and the + // vector was empty before this insert call. + // + if (flag == DB_KEYLAST) { + // pos == begin() && this was empty. + if(isempty) + flag = DB_AFTER; + else + flag = DB_BEFORE; + } + } + } + //@} + + /// \name Erase functions + /// The iterator pos in the functions must be a read-write iterator, + /// can't be read only. + /// \sa http://www.cplusplus.com/reference/stl/vector/erase.html + //@{ + /// \brief Erase element at position pos. + /// \param pos The valid position in the container's range to erase. + /// \return The next position after the erased element. + inline iterator erase (iterator pos) + { + if (pos == end_itr_) + return pos; + pos.pcsr_->del(); + ++pos; + // Synchronized with underlying cursor. + return pos; + } + + /// \brief Erase elements in range [first, last) + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + /// \return The next position after the erased elements. + iterator erase (iterator first, iterator last) + { + iterator itr; + int ret = 0; + Dbt k, d; + + // If ret is non-zero, it is because there is no element in + // this container any more. + // + for (itr = first; itr != last && ret == 0; ++itr) { + if (itr == end_itr_) + return itr; + ret = itr.pcsr_->del(); + } + return itr; + } + //@} + //////////////////////////////////////////////////////////////////// + + /// \brief Swap content with another vector vec. + /// \param vec The other vector to swap content with. + /// This function supports auto-commit. + /// \sa http://www.cplusplus.com/reference/stl/vector/swap.html + void swap (self& vec) + { + T tmp; + size_t sz, vsz, i, j, m; + self& me = *this; + self *longer, *shorter; + + verify_db_handles(vec); + this->begin_txn(); + try { + sz = this->size(); + vsz = vec.size(); + // do swap + for (i = 0; (i < sz) && (i < vsz); i++) { + tmp = me[(index_type)i]; + me[(index_type)i] = vec[(index_type)i]; + vec[(index_type)i] = tmp; + } + + // Move the longer vector's remaining part to the + // shorter one. + // + if (sz == vsz) + return; + else if (sz < vsz) { + longer = &vec; + shorter = &me; + j = vsz; + } else { + longer = &me; + shorter = &vec; + j = sz; + } + + self &lv = *longer; + self &sv = *shorter; + m = i; + for (; i < j; i++) + sv.push_back(lv[(index_type)i]); + + typename self::iterator itr1 = + lv.begin() + (int)m, itr2 = lv.end(); + + lv.erase(itr1, itr2); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + } + + // When DB_AUTO_COMMIT is set, no transaction needs to be begun to + // support autocommit because DB->truncate internally supports it. + /// Remove all elements of the vector, make it an empty vector. + /// This function supports auto-commit. + /// \param b_truncate Same as that of db_vector::assign(). + /// \sa http://www.cplusplus.com/reference/stl/vector/clear.html + void clear(bool b_truncate = true) + { + int ret; + u_int32_t flag; + DbEnv *penv = this->get_db_handle()->get_env(); + + if (b_truncate) { + ResourceManager::instance()->close_db_cursors( + this->get_db_handle()); + + BDBOP2(this->get_db_handle()->truncate( + ResourceManager::instance()->current_txn(penv), + NULL, 0), ret, this->abort_txn()); + } else { + ReadModifyWriteOption brmw( + ReadModifyWriteOption::no_read_modify_write()); + + BDBOP(penv->get_open_flags(&flag), ret); + + // DB_RMW flag requires locking subsystem. + if ((flag & DB_INIT_LOCK) || (flag & DB_INIT_CDB) || + (flag & DB_INIT_TXN)) + brmw = + ReadModifyWriteOption::read_modify_write(); + + try { // Truncate is capable of autocommit internally. + this->begin_txn(); + erase(begin(brmw, false), end()); + this->commit_txn(); + } catch (...) { + this->abort_txn(); + throw; + } + } + } + + //////////////////////////////////////////////////////////////////// + // + // Begin methods only defined in std::list class. + /// \name std::list specific functions + /// \sa http://www.cplusplus.com/reference/stl/list/ + //@{ + /// \brief Remove all elements whose values are "value" from the list. + /// + /// This function supports auto-commit. + /// \param value The target value to remove. + /// \sa http://www.cplusplus.com/reference/stl/list/remove/ + void remove(const T& value) + { + iterator i; + + try { + begin_txn(); + for (i = begin(); i != end(); ++i) + if (*i == value) + erase(i); + commit_txn(); + } catch (...) { + abort_txn(); + throw; + } + } + + /// \brief Remove all elements making "pred" return true. + /// + /// This function supports auto-commit. + /// \param pred The binary predicate judging elements in this list. + /// \sa http://www.cplusplus.com/reference/stl/list/remove_if/ + template <class Predicate> + void remove_if(Predicate pred) + { + iterator i; + + try { + begin_txn(); + for (i = begin(); i != end(); ++i) + if (pred(*i)) + erase(i); + commit_txn(); + } catch (...) { + abort_txn(); + throw; + } + } + + /// \brief Merge content with another container. + /// + /// This function supports auto-commit. + /// \param x The other list to merge with. + /// \sa http://www.cplusplus.com/reference/stl/list/merge/ + void merge(self& x) + { + DbstlListSpecialOps<T, value_type_sub> obj(this); + obj.merge(x); + } + + + /// \brief Merge content with another container. + /// + /// This function supports auto-commit. + /// \param x The other list to merge with. + /// \param comp The compare function to determine insertion position. + /// \sa http://www.cplusplus.com/reference/stl/list/merge/ + template <class Compare> + void merge(self& x, Compare comp) + { + verify_db_handles(x); + iterator itr, itrx; + + try { + begin_txn(); + for (itr = begin(), itrx = x.begin(); + itr != end_itr_ && itrx != x.end();) { + if (!comp(*itr, *itrx)) { + insert(itr, *itrx); + ++itrx; + } else + ++itr; + } + if (itr == end_itr_ && itrx != x.end()) + insert(itr, itrx, x.end()); + x.clear(); + commit_txn(); + } catch (...) { + abort_txn(); + throw; + } + } + + /// \brief Remove consecutive duplicate values from this list. + /// + /// This function supports auto-commit. + /// \sa http://www.cplusplus.com/reference/stl/list/unique/ + void unique() + { + DbstlListSpecialOps<T, value_type_sub> obj(this); + obj.unique(); + } + + /// \brief Remove consecutive duplicate values from this list. + /// + /// This function supports auto-commit. + /// \param binary_pred The compare predicate to dertermine uniqueness. + /// \sa http://www.cplusplus.com/reference/stl/list/unique/ + template <class BinaryPredicate> + void unique(BinaryPredicate binary_pred) + { + DbstlListSpecialOps<T, value_type_sub> obj(this); + obj.unique(binary_pred); + } + + /// \brief Sort this list. + /// + /// This function supports auto-commit. + /// \sa http://www.cplusplus.com/reference/stl/list/sort/ + void sort() + { + DbstlListSpecialOps<T, value_type_sub> obj(this); + obj.sort(); + } + + /// \brief Sort this list. + /// + /// This function supports auto-commit. + /// \param comp The compare operator to determine element order. + /// \sa http://www.cplusplus.com/reference/stl/list/sort/ + template <class Compare> + void sort(Compare comp) + { + DbstlListSpecialOps<T, value_type_sub> obj(this); + obj.sort(comp); + } + + /// \brief Reverse this list. + /// + /// This function supports auto-commit. + /// \sa http://www.cplusplus.com/reference/stl/list/reverse/ + void reverse() + { + try { + self tmp; + const self &cthis = *this; + const_reverse_iterator ri; + + begin_txn(); + for (ri = cthis.rbegin(BulkRetrievalOption:: + bulk_retrieval()); ri != rend(); ++ri) + tmp.push_back(*ri); + assign(tmp.begin(), tmp.end()); + commit_txn(); + } catch (...) { + abort_txn(); + throw; + } + } + + /// \brief Moves elements from list x into this list. + /// + /// Moves all elements in list x into this list + /// container at the + /// specified position, effectively inserting the specified + /// elements into the container and removing them from x. + /// This function supports auto-commit. + /// \param position Position within the container where the elements + /// of x are inserted. + /// \param x The other list container to splice from. + /// \sa http://www.cplusplus.com/reference/stl/list/splice/ + void splice (iterator position, self& x) + { + verify_db_handles(x); + try { + begin_txn(); + insert(position, x.begin(), x.end()); + x.clear(); + commit_txn(); + } catch (...) { + abort_txn(); + throw; + } + } + + /// \brief Moves elements from list x into this list. + /// + /// Moves elements at position i of list x into this list + /// container at the + /// specified position, effectively inserting the specified + /// elements into the container and removing them from x. + /// This function supports auto-commit. + /// \param position Position within the container where the elements + /// of x are inserted. + /// \param x The other list container to splice from. + /// \param i The position of element in x to move into this list. + /// \sa http://www.cplusplus.com/reference/stl/list/splice/ + void splice (iterator position, self& x, iterator i) + { + verify_db_handles(x); + try { + begin_txn(); + assert(!(i == x.end())); + insert(position, *i); + x.erase(i); + commit_txn(); + } catch (...) { + abort_txn(); + throw; + } + } + + /// \brief Moves elements from list x into this list. + /// + /// Moves elements in range [first, last) of list x into this list + /// container at the + /// specified position, effectively inserting the specified + /// elements into the container and removing them from x. + /// This function supports auto-commit. + /// \param position Position within the container where the elements + /// of x are inserted. + /// \param x The other list container to splice from. + /// \param first The range's closed boundary. + /// \param last The range's open boundary. + /// \sa http://www.cplusplus.com/reference/stl/list/splice/ + void splice (iterator position, self& x, iterator first, iterator last) + { + verify_db_handles(x); + try { + begin_txn(); + insert(position, first, last); + x.erase(first, last); + commit_txn(); + } catch (...) { + abort_txn(); + throw; + } + } + //@} + //////////////////////////////////////////////////////////////////// + +private: + typedef db_vector_iterator<T, value_type_sub> iterator_type; + typedef db_container base; + friend class db_vector_iterator<T, value_type_sub>; + friend class db_vector_base_iterator<T>; + friend class db_reverse_iterator<iterator, const_iterator>; + friend class db_reverse_iterator<const_iterator, iterator>; + friend class DbstlListSpecialOps<T, value_type_sub>; + + // Replace current contents with those in 'x'. + inline void copydb(const self&x) + { + const_iterator itr; + + // TODO: Make sure clear can succeed, it fails if there are + // cursors open in other threads. + clear(false); + for (itr = x.begin(); itr != x.end(); ++itr) + push_back(*itr); + } + + static iterator end_itr_; + + template <Typename InputIterator> + inline void push_range(InputIterator& first, InputIterator& last) + { + InputIterator itr; + + for (itr = first; itr != last; ++itr) + push_back(*itr); + } + + inline void push_range(const_iterator& first, const_iterator& last) + { + const_iterator itr; + + for (itr = first; itr != last; ++itr) + push_back(*itr); + + } + + // Move pos to last, pos must initially be the end() iterator. + inline void end_to_last(const const_iterator& pos) const + { + if (pos != end_itr_) + return; + pos.pcsr_.set_cursor(new TRandDbCursor()); + open_itr(pos); + pos.last(); + } + + // This function generate appropriate flags for cursor insert calls. + void make_insert_args(iterator& pos, u_int32_t& flag, bool &isempty) + { + isempty = false; + if (pos.itr_status_ == INVALID_ITERATOR_POSITION) { + ((self*)pos.owner_)->end_to_last(pos); + /* Empty db, iterator at "begin()". */ + if (((self*)pos.owner_)->empty()) { + flag = DB_KEYLAST; /* Empty */ + isempty = true; + } else + /* Move pos to last element. */ + flag = DB_AFTER; + } else + flag = DB_BEFORE; + } + + // Open iterator and move it to point the 1st key/data pair. + // + void open_itr(const const_iterator&itr, bool readonly = false) const + { + u_int32_t oflags = 0; + int ret; + DbEnv *penv = this->get_db_handle()->get_env(); + + itr.owner_ = (db_container*)this; + if (!readonly && penv != NULL) { + BDBOP((penv->get_open_flags(&oflags)), ret); + // Open a writeable cursor when in CDS mode and not + // requesting a read only iterator. + if ((oflags & DB_INIT_CDB) != 0) + ((self *)this)->set_cursor_open_flags( + this->get_cursor_open_flags() | + DB_WRITECURSOR); + } + if (!itr.pcsr_) + itr.pcsr_.set_cursor(new TRandDbCursor( + itr.bulk_retrieval_, + itr.rmw_csr_, itr.directdb_get_)); + itr.itr_status_ = itr.pcsr_->open((db_container*)this, + this->get_cursor_open_flags()); + } + + void open_itr(const reverse_iterator &itr, bool readonly = false) const + { + u_int32_t oflags = 0; + int ret; + DbEnv *penv = this->get_db_handle()->get_env(); + + itr.owner_ = (db_container*)this; + if (!readonly && penv != NULL) { + BDBOP((penv->get_open_flags(&oflags)) , ret); + // Open a writeable cursor when in CDS mode and not + // requesting a read only iterator. + if ((oflags & DB_INIT_CDB) != 0) + ((self *)this)->set_cursor_open_flags( + this->get_cursor_open_flags() | + DB_WRITECURSOR); + } + if (!itr.pcsr_) + itr.pcsr_.set_cursor(new TRandDbCursor( + itr.bulk_retrieval_, + itr.rmw_csr_, itr.directdb_get_)); + itr.itr_status_ = itr.pcsr_->open((db_container*)this, + this->get_cursor_open_flags()); + itr.update_cur_pair(); + } + + inline void init_itr(const_iterator &itr) const + { + itr.owner_ = (db_container*)this; + } + + // Certain flags and parameters need to be set to the database and + // environment handle for them to back-up a certain type of container. + // This function verifies that db and env handles are well configured + // to be suitable for this type of containers. + virtual const char* verify_config(Db*db, DbEnv*env) const + { + u_int32_t oflags, sflags, oflags2; + const char *errmsg = NULL; + int ret; + DBTYPE dbtype; + + errmsg = db_container::verify_config(db, env); + if (errmsg) + return errmsg; + + oflags = sflags = oflags2 = 0; + BDBOP((db->get_type(&dbtype)) || (db->get_open_flags(&oflags)) + || (db->get_flags(&sflags)) || + (env->get_open_flags(&oflags2)), ret); + + if (dbtype != DB_RECNO) + errmsg = "Must use DB_RECNO type of database."; + // DB_THREAD is not always required, only required if the db + // handle is shared among multiple threads, which is not a + // case we can detect here. + + return errmsg; + } + + +}; // db_vector + +template <Typename T, Typename value_type_sub> +typename db_vector<T, value_type_sub>::iterator + db_vector<T, value_type_sub>::end_itr_; + +// Partial spececialization version of std::swap for db_vector. +template <Typename T, Typename value_type_sub> +void swap(db_vector<T, value_type_sub>&v1, db_vector<T, value_type_sub>&v2) +{ + v1.swap(v2); +} + + + +template <typename T, typename T2> +class _exported DbstlListSpecialOps +{ + typedef db_vector<T, T2> partner; + typedef typename partner::iterator iterator; + partner *that; +public: + DbstlListSpecialOps(partner *that1) + { + that = that1; + } + + template <class BinaryPredicate> + void unique(BinaryPredicate binary_pred) + { + T t, t2; + + try { + that->begin_txn(); + iterator i = that->begin(); + t2 = *i; + ++i; + for (; i != that->end_itr_; ++i) { + if (binary_pred((t = *i), t2)) + that->erase(i); + else + t2 = t; + } + that->commit_txn(); + } catch (...) { + that->abort_txn(); + throw; + } + } + + void unique() + { + T t, t2; + + try { + that->begin_txn(); + iterator i = that->begin(); + t2 = *i; + ++i; + for (; i != that->end_itr_; ++i) { + if ((t = *i) == t2) + that->erase(i); + else + t2 = t; + } + that->commit_txn(); + } catch (...) { + that->abort_txn(); + throw; + } + } + + /// This function supports auto-commit. + void merge(partner& x) + { + that->verify_db_handles(x); + T b; + iterator itr, itrx; + + try { + that->begin_txn(); + for (itr = that->begin(), itrx = x.begin(); + itr != that->end_itr_ && itrx != x.end();) { + if (*itr > (b = *itrx)) { + that->insert(itr, b); + ++itrx; + } else + ++itr; + } + if (itr == that->end_itr_ && itrx != x.end()) + that->insert(itr, itrx, x.end()); + x.clear(); + that->commit_txn(); + } catch (...) { + that->abort_txn(); + throw; + } + } + + /// This function supports auto-commit. + void sort() + { + try { + that->begin_txn(); + std::sort(that->begin(), that->end()); + that->commit_txn(); + } catch (...) { + that->abort_txn(); + throw; + } + } + + /// This function supports auto-commit. + template <class Compare> + void sort(Compare comp) + { + try { + that->begin_txn(); + std::sort(that->begin(), that->end(), comp); + that->commit_txn(); + } catch (...) { + that->abort_txn(); + throw; + } + } + + + +}; + + +template <typename T, typename T2> +class _exported DbstlListSpecialOps<T*, T2> +{ + typedef db_vector<T*, T2> partner; + typedef typename partner::iterator iterator; + typedef typename partner::const_iterator const_iterator; + partner *that; + DbstlElemTraits<T> *inst; + typename DbstlElemTraits<T>::ElemSizeFunct sizef; + typename DbstlElemTraits<T>::SequenceLenFunct seqlenf; + typename DbstlElemTraits<T>::SequenceCopyFunct seqcopyf; + typename DbstlElemTraits<T>::SequenceCompareFunct seqcmpf; + + void seq_assign(T *&dest, const T*src) + { + size_t sz = 0; + size_t seql = seqlenf(src); + + + if (sizef == NULL) + sz = sizeof(T) * (seql + 1); + else { + + for (size_t i = 0; i < seql; i++) + sz += sizef(src[i]); + // Add space for terminating object, like '\0' + // for char *string. + T tmp; + sz += sizef(tmp); + } + dest = (T *)DbstlReAlloc(dest, sz); + memset(dest, 0, sz); + seqcopyf(dest, src, seql); + } + + template <typename T4> + class CompareInt + { + typename DbstlElemTraits<T4>::SequenceCompareFunct cmpf; + public: + CompareInt(typename DbstlElemTraits<T4>:: + SequenceCompareFunct cmpf1) + { + cmpf = cmpf1; + } + + bool operator()(const std::basic_string<T4, + DbstlElemTraits<T4> > + &a, const std::basic_string<T, DbstlElemTraits<T4> > &b) + { + return cmpf(a.c_str(), b.c_str()); + } + + }; + + template <typename T4, typename Compare> + class CompareInt2 + { + public: + Compare comp_; + + CompareInt2(Compare comp) + { + comp_ = comp; + } + + bool operator()(const std::basic_string<T4, + DbstlElemTraits<T4> > + &s1, const std::basic_string<T4, DbstlElemTraits<T4> >& s2) + { + return comp_(s1.c_str(), s2.c_str()); + } + + }; +public: + DbstlListSpecialOps(partner *that1) + { + that = that1; + + // Though he following settings are called in ResourceManager + // singleton initialization, we still have to call them here + // because the global variable in the dll is not the same one + // as the one in this application. + DbstlElemTraits<char> * cstarinst = + DbstlElemTraits<char>::instance(); + cstarinst->set_sequence_len_function(dbstl_strlen); + cstarinst->set_sequence_copy_function(dbstl_strcpy); + cstarinst->set_sequence_compare_function(dbstl_strcmp); + cstarinst->set_sequence_n_compare_function(dbstl_strncmp); + + DbstlElemTraits<wchar_t> *wcstarinst = + DbstlElemTraits<wchar_t>::instance(); + wcstarinst->set_sequence_copy_function(dbstl_wcscpy); + wcstarinst->set_sequence_len_function(dbstl_wcslen); + wcstarinst->set_sequence_compare_function(dbstl_wcscmp); + wcstarinst->set_sequence_n_compare_function(dbstl_wcsncmp); + + inst = DbstlElemTraits<T>::instance(); + sizef = inst->get_size_function(); + seqlenf = inst->get_sequence_len_function(); + seqcopyf = inst->get_sequence_copy_function(); + seqcmpf = inst->get_sequence_compare_function(); + } + + template <class BinaryPredicate> + void unique(BinaryPredicate binary_pred) + { + T *t2 = NULL; + + try { + that->begin_txn(); + iterator i = that->begin(); + + seq_assign(t2, *i); + ++i; + for (; i != that->end(); ++i) { + if (binary_pred(*i, t2)) + that->erase(i); + else + seq_assign(t2, *i); + } + that->commit_txn(); + free(t2); + } catch (...) { + that->abort_txn(); + free(t2); + throw; + } + } + + void unique() + { + T *t2 = NULL; + + try { + that->begin_txn(); + iterator i = that->begin(); + seq_assign(t2, *i); + ++i; + for (; i != that->end(); ++i) { + if (seqcmpf(*i, t2) == 0) + that->erase(i); + else + seq_assign(t2, *i); + } + that->commit_txn(); + free(t2); + } catch (...) { + that->abort_txn(); + free(t2); + throw; + } + } + + /// This function supports auto-commit. + void merge(partner& x) + { + that->verify_db_handles(x); + iterator itr, itrx; + + try { + that->begin_txn(); + for (itr = that->begin(), itrx = x.begin(); + itr != that->end() && itrx != x.end();) { + if (seqcmpf(*itr, *itrx) > 0) { + that->insert(itr, *itrx); + ++itrx; + } else + ++itr; + } + if (itr == that->end() && itrx != x.end()) + that->insert(itr, itrx, x.end()); + x.clear(); + that->commit_txn(); + } catch (...) { + that->abort_txn(); + throw; + } + } + + void sort() + { + try { + typedef std::basic_string<T, DbstlElemTraits<T> > + string_t; + CompareInt<T> comp(DbstlElemTraits<T>::instance()-> + get_sequence_compare_function()); + std::list<string_t> tmplist(that->size()); + + that->begin_txn(); + const_iterator itr; + const partner&cthat = *that; + typename std::list<string_t>::iterator itr1; + + for (itr = cthat.begin(BulkRetrievalOption:: + bulk_retrieval()), itr1 = tmplist.begin(); + itr1 != tmplist.end(); ++itr, ++itr1) + *itr1 = string_t(*itr); + + tmplist.sort(); + that->clear(false); + for (typename std::list<string_t>::iterator + it = tmplist.begin(); + it != tmplist.end(); ++it) + that->push_back((T*)(it->c_str())); + + that->commit_txn(); + } catch (...) { + that->abort_txn(); + throw; + } + } + + /// This function supports auto-commit. + template <class Compare> + void sort(Compare comp) + { + try { + typedef std::basic_string<T, DbstlElemTraits<T> > + string_t; + CompareInt2<T, Compare> comp2(comp); + + std::list<string_t> tmplist(that->size()); + that->begin_txn(); + const_iterator itr; + const partner&cthat = *that; + typename std::list<string_t>::iterator itr1; + + for (itr = cthat.begin(BulkRetrievalOption:: + bulk_retrieval()), itr1 = tmplist.begin(); + itr1 != tmplist.end(); ++itr, ++itr1) + *itr1 = string_t(*itr); + + tmplist.sort(comp2); + that->clear(false); + for (typename std::list<string_t>::iterator it = + tmplist.begin(); + it != tmplist.end(); ++it) + that->push_back((T*)(it->c_str())); + + that->commit_txn(); + } catch (...) { + that->abort_txn(); + throw; + } + } + + +}; +//@} //dbstl_containers +END_NS +#endif //_DB_STL_DB_VECTOR_H + |