/*- * 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 #include START_NS(dbstl) using std::list; using std::istream; using std::ostream; using std::sort; #define TRandDbCursor RandDbCursor // Forward Declarations // The default parameter is needed for the following code to work. template > class db_vector; template > class db_vector_iterator; template class DbCursor; template class RandDbCursor; template class ElementRef; template 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 _exported db_vector_base_iterator : public db_base_iterator { protected: // typedef's can't be put after where it is used. typedef db_vector_base_iterator self; typedef db_recno_t index_type; using db_base_iterator::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& 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 object if T is a class type or an ElementHolder /// 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 object if T is a class type or an ElementHolder /// 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 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 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 >; friend class db_vector >; friend class DbCursor; friend class RandDbCursor; friend class ElementRef; friend class ElementHolder; }; // db_vector_base_iterator<> template db_vector_base_iterator db_vector_base_iterator::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 _exported db_vector_iterator : public db_vector_base_iterator { protected: typedef db_vector_iterator self; typedef db_recno_t index_type; using db_base_iterator::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& vi) : db_vector_base_iterator(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(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() { curpair_._DB_STL_SetIterator(this); } db_vector_iterator(const db_vector_base_iterator&obj) : db_vector_base_iterator(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 object if T is a class type or an ElementHolder /// 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 object if T is a class type or an ElementHolder /// 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 object if T is a class type or /// an ElementHolder 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 base; typedef index_type size_type; typedef index_type key_type; typedef T data_type; typedef db_vector OwnerType; friend class db_vector; friend class DbCursor; friend class RandDbCursor; friend class ElementRef; friend class ElementHolder; // 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 db_vector_base_iterator operator+(typename db_vector_base_iterator:: difference_type n, db_vector_base_iterator itr) { db_vector_base_iterator i2 = itr; i2 += n; return i2; } template db_vector_iterator operator+( typename db_vector_iterator:: difference_type n, db_vector_iterator itr) { db_vector_iterator i2 = itr; i2 += n; return i2; } template db_reverse_iterator operator+(typename db_reverse_iterator::difference_type n, db_reverse_iterator itr) { db_reverse_iterator i2 = itr; // The db_reverse_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 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 class _exported db_vector: public db_container { private: typedef db_vector 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 iterator; typedef db_vector_base_iterator const_iterator; typedef index_type size_type; typedef db_reverse_iterator reverse_iterator; typedef const value_type_sub* const_pointer; typedef db_reverse_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::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::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::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::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 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 bool operator==(const db_vector& v2) const { bool ret; size_t sz; verify_db_handles(v2); typename self::iterator i1; typename db_vector::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 bool operator!=(const db_vector& 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 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 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 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 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 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 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 void unique(BinaryPredicate binary_pred) { DbstlListSpecialOps 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 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 void sort(Compare comp) { DbstlListSpecialOps 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 iterator_type; typedef db_container base; friend class db_vector_iterator; friend class db_vector_base_iterator; friend class db_reverse_iterator; friend class db_reverse_iterator; friend class DbstlListSpecialOps; // 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 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 db_vector::iterator db_vector::end_itr_; // Partial spececialization version of std::swap for db_vector. template void swap(db_vector&v1, db_vector&v2) { v1.swap(v2); } template class _exported DbstlListSpecialOps { typedef db_vector partner; typedef typename partner::iterator iterator; partner *that; public: DbstlListSpecialOps(partner *that1) { that = that1; } template 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 void sort(Compare comp) { try { that->begin_txn(); std::sort(that->begin(), that->end(), comp); that->commit_txn(); } catch (...) { that->abort_txn(); throw; } } }; template class _exported DbstlListSpecialOps { typedef db_vector partner; typedef typename partner::iterator iterator; typedef typename partner::const_iterator const_iterator; partner *that; DbstlElemTraits *inst; typename DbstlElemTraits::ElemSizeFunct sizef; typename DbstlElemTraits::SequenceLenFunct seqlenf; typename DbstlElemTraits::SequenceCopyFunct seqcopyf; typename DbstlElemTraits::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 class CompareInt { typename DbstlElemTraits::SequenceCompareFunct cmpf; public: CompareInt(typename DbstlElemTraits:: SequenceCompareFunct cmpf1) { cmpf = cmpf1; } bool operator()(const std::basic_string > &a, const std::basic_string > &b) { return cmpf(a.c_str(), b.c_str()); } }; template class CompareInt2 { public: Compare comp_; CompareInt2(Compare comp) { comp_ = comp; } bool operator()(const std::basic_string > &s1, const std::basic_string >& 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 * cstarinst = DbstlElemTraits::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 *wcstarinst = DbstlElemTraits::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::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 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 > string_t; CompareInt comp(DbstlElemTraits::instance()-> get_sequence_compare_function()); std::list tmplist(that->size()); that->begin_txn(); const_iterator itr; const partner&cthat = *that; typename std::list::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::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 void sort(Compare comp) { try { typedef std::basic_string > string_t; CompareInt2 comp2(comp); std::list tmplist(that->size()); that->begin_txn(); const_iterator itr; const partner&cthat = *that; typename std::list::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::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