diff options
author | Zhang Qiang <qiang.z.zhang@intel.com> | 2012-05-29 12:22:00 +0800 |
---|---|---|
committer | Zhang Qiang <qiang.z.zhang@intel.com> | 2012-05-29 12:22:00 +0800 |
commit | 02f0634ac29e19c68279e5544cac963e7f1203b8 (patch) | |
tree | b983472f94ef063cedf866d8ecfb55939171779d /stl | |
parent | e776056ea09ba0b6d9505ced6913c9190a12d632 (diff) | |
download | db4-1.0_post.tar.gz db4-1.0_post.tar.bz2 db4-1.0_post.zip |
Diffstat (limited to 'stl')
-rw-r--r-- | stl/dbstl_base_iterator.h | 498 | ||||
-rw-r--r-- | stl/dbstl_common.in | 453 | ||||
-rw-r--r-- | stl/dbstl_container.cpp | 520 | ||||
-rw-r--r-- | stl/dbstl_container.h | 582 | ||||
-rw-r--r-- | stl/dbstl_dbc.h | 1327 | ||||
-rw-r--r-- | stl/dbstl_dbt.h | 803 | ||||
-rw-r--r-- | stl/dbstl_element_ref.h | 873 | ||||
-rw-r--r-- | stl/dbstl_exception.h | 257 | ||||
-rw-r--r-- | stl/dbstl_inner_utility.h | 59 | ||||
-rw-r--r-- | stl/dbstl_map.h | 3395 | ||||
-rw-r--r-- | stl/dbstl_resource_manager.cpp | 1039 | ||||
-rw-r--r-- | stl/dbstl_resource_manager.h | 355 | ||||
-rw-r--r-- | stl/dbstl_set.h | 1583 | ||||
-rw-r--r-- | stl/dbstl_utility.h | 496 | ||||
-rw-r--r-- | stl/dbstl_vector.h | 3332 |
15 files changed, 15572 insertions, 0 deletions
diff --git a/stl/dbstl_base_iterator.h b/stl/dbstl_base_iterator.h new file mode 100644 index 0000000..577ce65 --- /dev/null +++ b/stl/dbstl_base_iterator.h @@ -0,0 +1,498 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#ifndef _DB_STL_DB_BASE_ITERATOR_H +#define _DB_STL_DB_BASE_ITERATOR_H + +#include "dbstl_common.h" + +START_NS(dbstl) + +template <Typename ddt> +class ElementRef; +template <Typename ddt> +class ElementHolder; + +/** \defgroup dbstl_iterators dbstl iterator classes +Common information for all dbstl iterators: + +1. Each instance of a dbstl iterator uniquely owns a Berkeley DB cursor, +so that the key/data pair it currently sits on is always valid before it moves +elsewhere. It also caches the current key/data pair values in order for member +functions like operator* /operator-> to work properly, but caching is not +compatible with standard C++ Stl behavior --- the C++ standard requires the +iterator refer to a shared piece of memory where the data is stored, +thus two iterators of the same container sitting on the same element should +point to the same memory location, which is false for dbstl iterators. + +2. There are some functions common to each child class of this class which have +identical behaviors, so we will document them here. + +@{ +This class is the base class for all dbstl iterators, there is no much to say +about this class itself, and users are not supposed to directly use this class +at all. So we will talk about some common functions of dbstl iterators in +this section. + +\sa db_vector_base_iterator db_vector_iterator db_map_base_iterator +db_map_iterator db_set_base_iterator db_set_iterator +*/ +template <Typename ddt> +class db_base_iterator +{ +protected: + typedef db_base_iterator<ddt> self; + friend class ElementHolder<ddt>; + friend class ElementRef<ddt>; + + // The container from which this iterator is created. + mutable db_container *owner_; + + bool dead_; // Internally used to prevent recursive destruction calls. + + // Whether or not to always get current key/data pair directly from db; + // If true, always poll db, slower but safe for concurrent use; + // otherwise faster, but when multiple iterators point to the same + // key/data pair (they definitely have same locker id) and use some + // iterators to update current key/data pair, then other iterators' + // key/data pairs are obsolete, need to call iterator::refresh(). + // + bool directdb_get_; + + // Whether to do bulk retrieval for a read only iterator. If non-zero, + // do bulk retrieval and use this member as the bulk buffer size, + // otherwise, do not use bulk retrieval. While doing bulk retrieval, + // no longer read from database even if directdb_get is true. + u_int32_t bulk_retrieval_; + + // Whether to use DB_RMW flag in Dbc::get. Users can set this flag in + // db_container::begin(). The flag may be ignored when there is no + // locking subsystem initialized. + bool rmw_csr_; + + // Whether this iterator is a db_set/db_multiset iterator. + bool is_set_; + + // Whether this iterator is read only. Default value is false. + // The const version of begin(), or passing an explicit "readonly" + // parameter to non-const begin() will will create a read only + // iterator; + // + bool read_only_; + + // Iteration status. If in valid range, it is 0, otherwise it is + // INVALID_ITERATOR_POSITION. + mutable int itr_status_; + + // Distinguish the invalid iterator of end() and rend(). If an + // iterator was invalidated in operator++, inval_pos_type_ is + // IPT_AFTER_LAST, else if in operator-- it is IPT_BEFORE_FIRST. + // For random iterator, inval_pos_type_ is also updated in random + // movement functions. This member is only valid when itr_status_ is + // set to INVALID_ITERATOR_POSITION. + mutable char inval_pos_type_; + // Values to denote the invalid positions. + const static char IPT_BEFORE_FIRST = -1; + const static char IPT_AFTER_LAST = 1; + const static char IPT_UNSET = 0; + + virtual void delete_me() const { delete this;} + + virtual db_base_iterator* dup_itr() const + { + THROW(InvalidFunctionCall, ( + "\ndb_base_iterator<>::dup_itr can't be called.\n")); + + } + + inline bool is_set_iterator() const + { + return is_set_; + } + + // The following two functions can't be abstract virtual for + // compiler errors. + virtual int replace_current(const ddt&) + { + THROW(InvalidFunctionCall, ( + "\ndb_base_iterator<>::replace_current can't be called\n")); + } + + virtual int replace_current_key(const ddt&) + { + THROW(InvalidFunctionCall, ( + "\ndb_base_iterator<>::replace_current_key can't be called\n")); + } + +public: +#if NEVER_DEFINE_THIS_MACRO_IT_IS_FOR_DOXYGEN_ONLY + /// Read data from underlying database via its cursor, and update + /// its cached value. + /// \param from_db Whether retrieve data from database rather than + /// using the cached data in this iterator. + /// \return 0 if succeeded. Otherwise an DbstlException exception + /// will be thrown. + int refresh(bool from_db = true); + + /** Close its cursor. If you are sure the iterator is no longer + used, call this function so that its underlying cursor is closed + before this iterator is destructed, potentially increase performance + and concurrency. Note that the cursor is definitely + closed at iterator destruction if you don't close it explicitly. + */ + void close_cursor() const; + + /** Call this function to modify bulk buffer size. Bulk retrieval is + enabled when creating an iterator, so users 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 buffer size in bytes. + \return true if succeeded, false otherwise. + */ + bool set_bulk_buffer(u_int32_t sz); + + /// Return current bulk buffer size. Returns 0 if bulk retrieval is + /// not enabled. + u_int32_t get_bulk_bufsize(); +#endif // NEVER_DEFINE_THIS_MACRO_IT_IS_FOR_DOXYGEN_ONLY + + //////////////////////////////////////////////////////////////////// + // + // Begin public constructors and destructor. + // + /// Default constructor. + db_base_iterator() + { + owner_ = NULL; + directdb_get_ = true; + dead_ = false; + itr_status_ = INVALID_ITERATOR_POSITION; + read_only_ = false; + is_set_ = false; + bulk_retrieval_ = 0; + rmw_csr_ = false; + inval_pos_type_ = IPT_UNSET; + } + + /// Constructor. + db_base_iterator(db_container *powner, bool directdbget, + bool b_read_only, u_int32_t bulk, bool rmw) + { + owner_ = powner; + directdb_get_ = directdbget; + dead_ = false; + itr_status_ = INVALID_ITERATOR_POSITION; + read_only_ = b_read_only; + is_set_ = false; + bulk_retrieval_ = bulk; + rmw_csr_ = rmw; + inval_pos_type_ = IPT_UNSET; + } + + /// Copy constructor. Copy all members of this class. + db_base_iterator(const db_base_iterator &bi) + { + owner_ = bi.owner_; + directdb_get_ = bi.directdb_get_; + dead_ = false; + itr_status_ = bi.itr_status_; + read_only_ = bi.read_only_; + is_set_ = bi.is_set_; + bulk_retrieval_ = bi.bulk_retrieval_; + rmw_csr_ = bi.rmw_csr_; + inval_pos_type_ = bi.inval_pos_type_; + } + + /** + Iterator assignment operator. + Iterator assignment will cause the underlying cursor of the right iterator + to be duplicated to the left iterator after its previous cursor is closed, + to make sure each iterator owns one unique cursor. The key/data cached + in the right iterator is copied to the left iterator. Consequently, + the left iterator points to the same key/data pair in the database + as the the right value after the assignment, and have identical cached + key/data pair. + \param bi The other iterator to assign with. + \return The iterator bi's reference. + */ + inline const self& operator=(const self& bi) + { + ASSIGNMENT_PREDCOND(bi) + owner_ = bi.owner_; + directdb_get_ = bi.directdb_get_; + dead_ = false; + itr_status_ = bi.itr_status_; + read_only_ = bi.read_only_; + is_set_ = bi.is_set_; + bulk_retrieval_ = bi.bulk_retrieval_; + rmw_csr_ = bi.rmw_csr_; + inval_pos_type_ = bi.inval_pos_type_; + return bi; + } + + /// Destructor. + virtual ~db_base_iterator() {} + + //////////////////////////////////////////////////////////////////// + + /// \brief Get bulk buffer size. + /// + /// Return bulk buffer size. If the size is 0, bulk retrieval is not + /// enabled. + u_int32_t get_bulk_retrieval() const { return bulk_retrieval_; } + + /// \brief Get DB_RMW setting. + /// + /// Return true if the iterator's cursor has DB_RMW flag set, false + /// otherwise. DB_RMW flag causes + /// a write lock to be acquired when reading a key/data pair, so + /// that the transaction won't block later when writing back the + /// updated value in a read-modify-write operation cycle. + bool is_rmw() const { return rmw_csr_; } + + /// \brief Get direct database get setting. + /// + /// Return true if every operation to retrieve the key/data pair the + /// iterator points to will read from database rather than using + /// the cached value, false otherwise. + bool is_directdb_get() const {return directdb_get_; } +}; // db_base_iterator + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// +// db_reverse_iterator class template definition. +// +// db_reverse_iterator is the reverse iterator adapter for all iterator +// classes of dbstl. It makes an original iterator its reverse iterator. +// We don't want to use std::reverse_iterator<> adapter because it is more +// more expensive. Here we move the original iterator one position back to +// avoid unnecessary movement. +// +/// This class is the reverse class adaptor for all dbstl iterator classes. +/// It inherits from real iterator classes like db_vector_iterator, +/// db_map_iterator or db_set_iterator. When you call container::rbegin(), +/// you will get an instance of this class. +/// \sa db_vector_base_iterator db_vector_iterator db_map_base_iterator +/// db_map_iterator db_set_base_iterator db_set_iterator +template <Typename iterator, Typename twin_itr_t> +class _exported db_reverse_iterator : public iterator +{ +public: + // typedefs are not inherited, so re-define them here. + // + typedef db_reverse_iterator<iterator, twin_itr_t> self; + typedef typename iterator::value_type T; + typedef iterator iterator_type; + typedef typename iterator::value_type value_type; + typedef typename iterator::reference reference; + typedef typename iterator::pointer pointer; + typedef typename iterator::iterator_category iterator_category; + typedef typename iterator::key_type key_type; + typedef typename iterator::data_type data_type; + typedef typename iterator::difference_type difference_type; + typedef typename iterator::value_type_wrap value_type_wrap; + // Construct a reverse iterator from iterator vi. We don't duplicate + // itrerator vi here because vi is not supposed to be used again. + // This function is supposed to be used by dbstl users, internally + // self::set_iterator method is used. + /// Constructor. Construct from an iterator of wrapped type. + db_reverse_iterator(const iterator& vi) : iterator(vi) + { + iterator::operator--(); + } + + /// Copy constructor. + db_reverse_iterator(const self& ritr) + : iterator(ritr) + { + } + + /// Copy constructor. + db_reverse_iterator(const + db_reverse_iterator<twin_itr_t, iterator>& ritr) + : iterator(ritr) + { + } + + /// Default constructor. + db_reverse_iterator() : iterator() + { + } + + // Do not throw exceptions here because it is normal to iterate to + // "end()". + // + /// \name Reverse iterator movement functions + /// When we talk about reverse 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. + //@{ + /// Move this iterator forward by one element. + /// \return The moved iterator at new position. + self& operator++() + { + iterator::operator--(); + return *this; + } + + /// Move this iterator forward by one element. + /// \return The original iterator at old position. + self operator++(int) + { + // XXX!!! This conversion relies on this class or + // db_set_iterator<> having no data members at all, which + // is currently the case. + return (self)iterator::operator--(1); + } + + /// Move this iterator backward by one element. + /// \return The moved iterator at new position. + self& operator--() + { + iterator::operator++(); + return *this; + } + + /// Move this iterator backward by one element. + /// \return The original iterator at old position. + self operator--(int) + { + // XXX!!! This conversion relies on this class or + // db_set_iterator<> having no data members at all, which + // is currently the case. + return (self)iterator::operator++(1); + } + //@} // Movement operators. + + /// Assignment operator. + /// \param ri The iterator to assign with. + /// \return The iterator ri. + /// \sa db_base_iterator::operator=(const self&) + const self& operator=(const self& ri) + { + ASSIGNMENT_PREDCOND(ri) + iterator::operator=(ri); + return ri; + } + + ////////////// Methods below only applies to random iterators. ///// + /// \name Operators for random reverse iterators + /// Return a new iterator by moving this iterator backward or forward + /// by n elements. + //@{ + /// Iterator shuffle operator. + /// Return a new iterator by moving this iterator forward + /// by n elements. + /// \param n The amount and direction of movement. If negative, + /// will move towards reverse direction. + /// \return A new iterator at new position. + self operator+(difference_type n) const + { + self ritr(*this); + ritr.iterator::operator-=(n); + return ritr; + } + + /// Iterator shuffle operator. + /// Return a new iterator by moving this iterator backward + /// by n elements. + /// \param n The amount and direction of movement. If negative, + /// will move towards reverse direction. + /// \return A new iterator at new position. + self operator-(difference_type n) const + { + self ritr(*this); + ritr.iterator::operator+=(n); + return ritr; + } + //@} + /// \name Operators for random reverse iterators + /// Move this iterator backward or forward by n elements and then + /// return it. + //@{ + /// Iterator shuffle operator. + /// Move this iterator forward by n elements and then + /// return it. + /// \param n The amount and direction of movement. If negative, + /// will move towards reverse direction. + /// \return This iterator at new position. + const self& operator+=(difference_type n) + { + iterator::operator-=(n); + return *this; + } + + /// Iterator shuffle operator. + /// Move this iterator backward by n elements and then + /// return it. + /// \param n The amount and direction of movement. If negative, + /// will move towards reverse direction. + /// \return This iterator at new position. + const self& operator-=(difference_type n) + { + iterator::operator+=(n); + return *this; + } + //@} + /// \name Operators for random reverse iterators + /// Reverse iterator comparison against reverse iterator itr, the one + /// sitting on elements with less index is returned to be greater. + //@{ + /// Less compare operator. + bool operator<(const self& itr) const + { + return (iterator::operator>(itr)); + } + + /// Greater compare operator. + bool operator>(const self& itr) const + { + return (iterator::operator<(itr)); + } + + /// Less equal compare operator. + bool operator<=(const self& itr) const + { + return (iterator::operator>=(itr)); + } + + /// Greater equal compare operator. + bool operator>=(const self& itr) const + { + return (iterator::operator<=(itr)); + } + //@} + // Because v.rend() - v.rbegin() < 0, we should negate base version. + /// Return the negative value of the difference of indices of elements + /// this iterator and itr are sitting on. + /// \return itr.index - this->index. + /// \param itr The other reverse iterator. + difference_type operator-(const self&itr) const + { + difference_type d = iterator::operator-(itr); + return -d; + } + + /// Return the reference of the element which can be reached by moving + /// this reverse iterator by Off times backward. If Off is negative, + /// the movement will be forward. + inline value_type_wrap operator[](difference_type Off) const + { + self ritr(*this); + value_type_wrap res = ritr.iterator::operator[](-Off); + return res; + } + +}; // reverse_iterator +//@} // dbstl_iterators +END_NS + +#endif // !_DB_STL_DB_BASE_ITERATOR_H diff --git a/stl/dbstl_common.in b/stl/dbstl_common.in new file mode 100644 index 0000000..1712a48 --- /dev/null +++ b/stl/dbstl_common.in @@ -0,0 +1,453 @@ +#ifndef _DB_STL_COMMON_H +#define _DB_STL_COMMON_H + +#ifdef DBSTL_DEBUG_LEAK +#include "vld.h" +#endif + +#include <assert.h> + +#include "db_cxx.h" + +// In release builds, the native assert will be disabled so we +// can't use it in dbstl in cases where we rely on the expression being +// evaluated to change the state of the application. +// +#if !defined(DEBUG) && !defined(_DEBUG) +#undef dbstl_assert +#define dbstl_assert(expression) +#else +#undef dbstl_assert +#define dbstl_assert(expression) do { \ + if (!(expression)) { \ + FailedAssertionException ex(__FILE__, __LINE__, #expression);\ + throw ex; } } while (0) +#endif + +#ifndef SIZE_T_MAX +// The max value for size_t variables, one fourth of 2 powers 32. +#define SIZE_T_MAX 1073741824 +#endif + +#if defined( DB_WIN32) || defined(_WIN32) +#include <windows.h> +#include <tchar.h> +#else +#define TCHAR char +#define _T(e) (e) +#define _ftprintf fprintf +#define _snprintf snprintf +#define _tcschr strchr +#define _tcscmp strcmp +#define _tcscpy strcpy +#define _tcslen strlen +#define _tgetopt getopt +#define _tmain main +#define _tprintf printf +#define _ttoi atoi +#endif + +// Macro for HAVE_WSTRING (detected by configure) +@WSTRING_decl@ + +// Thread local storage modifier declaration. +@TLS_decl@ +@TLS_defn@ + +#if !defined(TLS_DECL_MODIFIER) && !defined(HAVE_PTHREAD_TLS) +#error "No appropriate TLS modifier defined." +#endif + +////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// C++ compiler portability control macro definitions. +// If a C++ compiler does not support the following capabilities, disabling +// these flags will remove usage of the feature from DB STL. +// Where possible a DB STL has implemented work-arounds for the missing +// functionality. +// +#define HAVE_EXPLICIT_KEYWORD 1 +#define HAVE_NAMESPACE 1 +#define HAVE_TYPENAME 1 + +// Platform specific compiler capability configuration. +#ifdef WIN32 +#define CLS_SCOPE(clstmpl_name) +#else + +// C++ standard: It is not possible to define a full specialized version of +// a member function of a class template inside the class body. It needs to +// be defined outside the class template, and must be defined in the namespace +// scope. +#define CLS_SCOPE(clstmpl_name) clstmpl_name:: +#define NO_IN_CLASS_FULL_SPECIALIZATION 1 +#define NO_MEMBER_FUNCTION_PARTIAL_SPECIALIZATION 1 +#endif + +#if HAVE_NAMESPACE +#define START_NS(nsname) namespace nsname { +#define END_NS } +#else +#define START_NS(nsname) struct nsname { +#define END_NS }; +#endif + +#if HAVE_EXPLICIT_KEYWORD +#define EXPLICIT explicit +#else +#define EXPLICIT +#endif + +#if HAVE_TYPENAME +#define Typename typename +#else +#define Typename class +#endif + +////////////////////////////////////////////////////////////////////////// +// End of compiler portability control macro definitions. +//////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// Iterator status macro definitions. +// +#define INVALID_ITERATOR_POSITION -1 // Iterator goes out of valid range. +#define INVALID_ITERATOR_CURSOR -2 // The iterator's dbc cursor is invalid. +#define ITERATOR_DUP_ERROR -3 // Failed to duplicate a cursor. + +// Current cursor's key or data dbt has no data. +#define INVALID_KEY_DATA -4 +#define EMPTY_DBT_DATA -5 // Current cursor's pointed data dbt has no data. +#define ITERATOR_AT_END -6 +#define CURSOR_NOT_OPEN -7 + +/////////////////////////////////////////////////////////////////////// +// End of iterator status macro definitions. +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// +// +// Helper macros definitions. +// +// Use BDBOP and BDBOP2 to wrap Berkeley DB calls. The macros validate the +// return value. On failure, the wrappers clean up, and generate the +// expected exception. +// +#define BDBOP(bdb_call, ret) do { \ + if ((ret = (bdb_call)) != 0) throw_bdb_exception(#bdb_call, ret);\ + } while(0) +#define BDBOP2(bdb_call, ret, cleanup) do { \ + if ((ret = (bdb_call)) != 0) { (cleanup); \ + throw_bdb_exception(#bdb_call, ret);} \ + } while (0) +// Do not throw the exception if bdb_call returned a specified error number. +#define BDBOP3(bdb_call, ret, exception, cleanup) do { \ + if (((ret = (bdb_call)) != 0) && (ret & exception) == 0) { \ + (cleanup); throw_bdb_exception(#bdb_call, ret);} \ + } while (0) + +#define THROW(exception_type, arg_list) do { \ + exception_type ex arg_list; throw ex; } while (0) + +#define THROW0(exception_type) do { \ + exception_type ex; throw ex; } while (0) + +#define INVALID_INDEX ((index_type)-1) +#define INVALID_DLEN ((u_int32_t)-1) + +#define DBSTL_MAX_DATA_BUF_LEN 1024 * 4096 +#define DBSTL_MAX_KEY_BUF_LEN 1024 * 4096 +#define DBSTL_MAX_MTX_ENV_MUTEX 4096 * 4 +#define DBSTL_BULK_BUF_SIZE 256 * 1024 + +#define COMPARE_CHECK(obj) if (this == &obj) return true; +#define ASSIGNMENT_PREDCOND(obj) if (this == &obj) return obj; +////////////////////////////////////////////////////////////////// +// End of helper macro definitions. +////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// +// +// Public global function declarations. +// These functions are open/public functionalities of dbstl for +// dbstl users to call. +// +START_NS(dbstl) +// _exported is a macro we employ from db_cxx.h of Berkeley DB C++ +// API. If we want to export the symbols it decorates on Windows, +// we must define the macro "DB_CREATE_DLL", as is defined in dbstl +// project property. +/// \defgroup dbstl_global_functions dbstl global public functions +//@{ + +/// \name Functions to close database/environments. +/// Normally you don't have to close any database +/// or environment handles, they will be closed automatically. +/// Though you still have the following API to close them. +//@{ +/// Close pdb regardless of reference count. You must make sure pdb +/// is not used by others before calling this method. +/// You can close the underlying database of a container and assign +/// another database with right configurations to it, if the configuration +/// is not suitable for the container, there will be an +/// InvalidArgumentException type of exception thrown. +/// You can't use the container after you called close_db and before setting +/// another valid database handle to the container via +/// db_container::set_db_handle() function. +/// \param pdb The database handle to close. +_exported void close_db(Db *pdb); + +/// Close all open database handles regardless of reference count. +/// You can't use any container after you called close_all_dbs and +/// before setting another valid database handle to the +/// container via db_container::set_db_handle() function. +/// \sa close_db(Db *); +_exported void close_all_dbs(); + +/// \brief Close specified database environment handle regardless of reference +/// count. +/// +/// Make sure the environment is not used by any other databases. +/// \param pdbenv The database environment handle to close. +_exported void close_db_env(DbEnv *pdbenv); + +/// \brief Close all open database environment handles regardless of +/// reference count. +/// +/// You can't use the container after you called close_db and before setting +/// another valid database handle to the container via +/// db_container::set_db_handle() function. \sa close_db_env(DbEnv *); +_exported void close_all_db_envs(); +//@} + +/// \name Transaction control global functions. +/// dbstl transaction API. You should call these API rather than DB C/C++ +/// API to use Berkeley DB transaction features. +//@{ +/// Begin a new transaction from the specified environment "env". +/// This function is called by dbstl user to begin an external transaction. +/// The "flags" parameter is passed to DbEnv::txn_begin(). +/// If a transaction created from +/// the same database environment already exists and is unresolved, +/// the new transaction is started as a child transaction of that transaction, +/// and thus you can't specify the parent transaction. +/// \param env The environment to start a transaction from. +/// \param flags It is set to DbEnv::txn_begin() function. +/// \return The newly created transaction. +/// +_exported DbTxn* begin_txn(u_int32_t flags, DbEnv *env); + +/// Commit current transaction opened in the environment "env". +/// This function is called by user to commit an external explicit transaction. +/// \param env The environment whose current transaction is to be committed. +/// \param flags It is set to DbTxn::commit() funcion. +/// \sa commit_txn(DbEnv *, DbTxn *, u_int32_t); +/// +_exported void commit_txn(DbEnv *env, u_int32_t flags = 0); + +/// Commit a specified transaction and all its child transactions. +/// \param env The environment where txn is started from. +/// \param txn The transaction to commit, can be a parent transaction of a +/// nested transaction group, all un-aborted child transactions of +/// it will be committed. +/// \param flags It is passed to each DbTxn::commit() call. +/// \sa commit_txn(DbEnv *, u_int32_t); +_exported void commit_txn(DbEnv *env, DbTxn *txn, u_int32_t flags = 0); + +/// Abort current transaction of environment "env". This function is called by +/// dbstl user to abort an outside explicit transaction. +/// \param env The environment whose current transaction is to be aborted. +/// \sa abort_txn(DbEnv *, DbTxn *); +_exported void abort_txn(DbEnv *env); + +/// Abort specified transaction "txn" and all its child transactions. +/// That is, "txn" can be a parent transaction of a nested transaction group. +/// \param env The environment where txn is started from. +/// \param txn The transaction to abort, can be a parent transaction of a +/// nested transaction group, all child transactions of it will be aborted. +/// \sa abort_txn(DbEnv *); +/// +_exported void abort_txn(DbEnv *env, DbTxn *txn); + +/// Get current transaction of environment "env". +/// \param env The environment whose current transaction we want to get. +/// \return Current transaction of env. +_exported DbTxn* current_txn(DbEnv *env); + +/// Set environment env's current transaction handle to be newtxn. The original +/// transaction handle returned without aborting or commiting. This function +/// is used for users to use one transaction among multiple threads. +/// \param env The environment whose current transaction to replace. +/// \param newtxn The new transaction to be as the current transaction of env. +/// \return The old current transaction of env. It is not resolved. +_exported DbTxn* set_current_txn_handle(DbEnv *env, DbTxn *newtxn); +//@} + +/// \name Functions to open and register database/environment handles. +//@{ +/// Register a Db handle "pdb1". This handle and handles opened in it will be +/// closed by ResourceManager, so application code must not try to close or +/// delete it. Users can do enough configuration before opening the Db then +/// register it via this function. +/// All database handles should be registered via this function in each +/// thread using the handle. The only exception is the database handle opened +/// by dbstl::open_db should not be registered in the thread of the +/// dbstl::open_db call. +/// \param pdb1 The database handle to register into dbstl for current thread. +/// +_exported void register_db(Db *pdb1); + +/// Register a DbEnv handle env1, this handle and handles opened in it will be +/// closed by ResourceManager. Application code must not try to close or delete +/// it. Users can do enough config before opening the DbEnv and then register +/// it via this function. +/// All environment handles should be registered via this function in each +/// thread using the handle. The only exception is the environment handle +/// opened by dbstl::open_db_env should not be registered in the thread of +/// the dbstl::open_db_env call. +/// \param env1 The environment to register into dbstl for current thread. +/// +_exported void register_db_env(DbEnv *env1); + +/// Helper function to open a database and register it into dbstl for the +/// calling thread. +/// Users still need to register it in any other thread using it if it +/// is shared by multiple threads, via register_db() function. +/// Users don't need to delete or free the memory of the returned object, +/// dbstl will take care of that. +/// When you don't use dbstl::open_db() but explicitly call DB C++ API to +/// open a database, you must new the Db object, rather than create it +/// on stack, and you must delete the Db object by yourself. +/// \param penv The environment to open the database from. +/// \param cflags The create flags passed to Db class constructor. +/// \param filename The database file name, passed to Db::open. +/// \param dbname The database name, passed to Db::open. +/// \param dbtype The database type, passed to Db::open. +/// \param oflags The database open flags, passed to Db::open. +/// \param mode The database open mode, passed to Db::open. +/// \param txn The transaction to open the database from, passed to Db::open. +/// \param set_flags The flags to be set to the created database handle. +/// \return The opened database handle. +/// \sa register_db(Db *); +/// \sa open_db_env; +/// +_exported Db* open_db (DbEnv *penv, const char *filename, DBTYPE dbtype, + u_int32_t oflags, u_int32_t set_flags, int mode = 0644, DbTxn *txn = NULL, + u_int32_t cflags = 0, const char* dbname = NULL); + +/// Helper function to open an environment and register it into dbstl for the +/// calling thread. Users still need to register it in any other thread if it +/// is shared by multiple threads, via register_db_env() function above. +/// Users don't need to delete or free the memory of the returned object, +/// dbstl will take care of that. +/// +/// When you don't use dbstl::open_env() but explicitly call DB C++ API to +/// open an environment, you must new the DbEnv object, rather than create it +/// on stack, and you must delete the DbEnv object by yourself. +/// \param env_home Environment home directory, it must exist. Passed to +/// DbEnv::open. +/// \param cflags DbEnv constructor creation flags, passed to DbEnv::DbEnv. +/// \param set_flags Flags to set to the created environment before opening it. +/// \param oflags Environment open flags, passed to DbEnv::open. +/// \param mode Environment region files mode, passed to DbEnv::open. +/// \param cachesize Environment cache size, by default 4M bytes. +/// \return The opened database environment handle. +/// \sa register_db_env(DbEnv *); +/// \sa open_db; +/// +_exported DbEnv* open_env(const char *env_home, u_int32_t set_flags, + u_int32_t oflags = DB_CREATE | DB_INIT_MPOOL, + u_int32_t cachesize = 4 * 1024 * 1024, + int mode = 0644, + u_int32_t cflags = 0/* Flags for DbEnv constructor. */); +//@} + +/// @name Mutex API based on Berkeley DB mutex. +/// These functions are in-process mutex support which uses Berkeley DB +/// mutex mechanisms. You can call these functions to do portable +/// synchronization for your code. +//@{ +/// Allocate a Berkeley DB mutex. +/// \return Berkeley DB mutex handle. +_exported db_mutex_t alloc_mutex(); +/// Lock a mutex, wait if it is held by another thread. +/// \param mtx The mutex handle to lock. +/// \return 0 if succeed, non-zero otherwise, call db_strerror to get message. +_exported int lock_mutex(db_mutex_t mtx); +/// Unlock a mutex, and return immediately. +/// \param mtx The mutex handle to unlock. +/// \return 0 if succeed, non-zero otherwise, call db_strerror to get message. +_exported int unlock_mutex(db_mutex_t mtx); +/// Free a mutex, and return immediately. +/// \param mtx The mutex handle to free. +/// \return 0 if succeed, non-zero otherwise, call db_strerror to get message. +_exported void free_mutex(db_mutex_t mtx); +//@} + +/// Close cursors opened in dbp1. +/// \param dbp1 The database handle whose active cursors to close. +/// \return The number of cursors closed by this call. +_exported size_t close_db_cursors(Db* dbp1); + +/// \name Other global functions. +//@{ +/// If there are multiple threads within a process that make use of dbstl, then +/// this function should be called in a single thread mutual exclusively before +/// any use of dbstl in a process; Otherwise, you don't need to call it, but +/// are allowed to call it anyway. +_exported void dbstl_startup(); + +/// This function releases any memory allocated in the heap by code of dbstl. +/// So you can only call dbstl_exit() right before the entire process exits. +/// It will release any memory allocated by dbstl that have to live during +/// the entire process lifetime. +_exported void dbstl_exit(); + +/// Operators to compare two Dbt objects. +/// \param d1 Dbt object to compare. +/// \param d2 Dbt object to compare. +_exported bool operator==(const Dbt&d1, const Dbt&d2); +/// Operators to compare two DBT objects. +/// \param d1 DBT object to compare. +/// \param d2 DBT object to compare. +_exported bool operator==(const DBT&d1, const DBT&d2); + +/// If exisiting random temporary database name generation mechanism is still +/// causing name clashes, users can set this global suffix number which will +/// be append to each temporary database file name and incremented after each +/// append, and by default it is 0. +/// \param num Starting number to append to each temporary db file name. +_exported void set_global_dbfile_suffix_number(u_int32_t num); +//@} + +//@} // dbstl_global_functions + +// Internally used memory allocation functions, they will throw an exception +// of NotEnoughMemoryException if can't allocate memory. +_exported void * DbstlReAlloc(void *ptr, size_t size); +_exported void * DbstlMalloc(size_t size); + +_exported u_int32_t hash_default(Db * /*dbp*/, const void *key, u_int32_t len); + +// Default string manipulation callbacks. +_exported u_int32_t dbstl_strlen(const char *str); +_exported void dbstl_strcpy(char *dest, const char *src, size_t num); +_exported int dbstl_strncmp(const char *s1, const char *s2, size_t num); +_exported int dbstl_strcmp(const char *s1, const char *s2); +_exported int dbstl_wcscmp(const wchar_t *s1, const wchar_t *s2); +_exported int dbstl_wcsncmp(const wchar_t *s1, const wchar_t *s2, size_t num); +_exported u_int32_t dbstl_wcslen(const wchar_t *str); +_exported void dbstl_wcscpy(wchar_t *dest, const wchar_t *src, size_t num); + +END_NS + +////////////////////////////////////////////////////////////////// +// End of public global function declarations. +////////////////////////////////////////////////////////////////// + +#endif /* !_DB_STL_COMMON_H */ diff --git a/stl/dbstl_container.cpp b/stl/dbstl_container.cpp new file mode 100644 index 0000000..4543f79 --- /dev/null +++ b/stl/dbstl_container.cpp @@ -0,0 +1,520 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#include <string> + +#include "dbstl_container.h" +#include "dbstl_resource_manager.h" +#include "dbstl_exception.h" +#include "dbstl_utility.h" +#include "dbstl_inner_utility.h" + +typedef struct { + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +} db_timespec; + +extern "C"{ +void __os_id (DB_ENV *, pid_t *, db_threadid_t*); +void __os_gettime(ENV *env, db_timespec *tp, int monotonic); +} + +START_NS(dbstl) + +using std::string; +u_int32_t db_container::g_db_file_suffix_ = 0; + +void set_global_dbfile_suffix_number(u_int32_t num) +{ + db_container::g_db_file_suffix_ = num; +} + +// Internally used memory allocation functions, they will throw an exception +// of NotEnoughMemoryException if can't allocate memory. +void *DbstlReAlloc(void *ptr, size_t size) +{ + void *p; + + assert(size != 0); + if ((p = realloc(ptr, size)) == NULL) + THROW(NotEnoughMemoryException, + ("DbstlReAlloc failed to allocate memory", size)); + + return p; +} + +void *DbstlMalloc(size_t size) +{ + void *p; + + assert(size != 0); + if ((p = malloc(size)) == NULL) + THROW(NotEnoughMemoryException, + ("DbstlMalloc failed to allocate memory", size)); + return p; +} + +void db_container::init_members() +{ + txn_begin_flags_ = 0; + commit_flags_ = 0; + cursor_oflags_ = 0; + pdb_ = NULL; + is_set_ = false; + auto_commit_ = false; + dbenv_ = NULL; +} + +void db_container::init_members(Db *dbp, DbEnv *envp) +{ + txn_begin_flags_ = 0; + commit_flags_ = 0; + is_set_ = false; + cursor_oflags_ = 0; + pdb_ = dbp; + dbenv_ = envp; + set_auto_commit(pdb_); +} + +db_container::db_container() +{ + init_members(); +} + +db_container::db_container(const db_container& dbctnr) +{ + init_members(dbctnr); +} + +db_container::db_container(Db *dbp, DbEnv *envp) +{ + init_members(dbp, envp); +} + +void db_container::init_members(const db_container&dbctnr) +{ + txn_begin_flags_ = dbctnr.txn_begin_flags_; + commit_flags_ = dbctnr.commit_flags_; + cursor_oflags_ = dbctnr.cursor_oflags_; + // We don't copy database handles because we will clone another + // database from dbctnr's db, and get its handle. We will + // copy the following database properties because they will be + // definitely identical. + // + pdb_ = NULL; + is_set_ = dbctnr.is_set_; + auto_commit_ = dbctnr.auto_commit_; + dbenv_ = dbctnr.dbenv_; +} +void db_container::open_db_handles(Db *&pdb, DbEnv *&penv, DBTYPE dbtype, + u_int32_t oflags, u_int32_t sflags) +{ + if (pdb == NULL) { + pdb = open_db(penv, NULL, dbtype, oflags, sflags); + this->pdb_ = pdb; + } + + if (penv == NULL) { + penv = pdb->get_env(); + this->dbenv_ = penv; + set_auto_commit(pdb_); + } +} + +Db* db_container::clone_db_config(Db *dbp) +{ + string str; + + return clone_db_config(dbp, str); +} + +// Open a new db with identical configuration to dbp. The dbfname brings +// back the generated db file name. +Db* db_container::clone_db_config(Db *dbp, string &dbfname) +{ + Db *tdb = NULL; + int ret; + DBTYPE dbtype; + u_int32_t oflags, sflags; + const char *dbfilename, *dbname, *tdbname; + + BDBOP2(dbp->get_type(&dbtype), ret, dbp->close(0)); + BDBOP2(dbp->get_open_flags(&oflags), ret, dbp->close(0)); + BDBOP2(dbp->get_flags(&sflags), ret, dbp->close(0)); + + BDBOP (dbp->get_dbname(&dbfilename, &dbname), ret); + if (dbfilename == NULL) { + tdb = open_db(dbp->get_env(), + dbfilename, dbtype, oflags, sflags, 0420, NULL, 0, dbname); + dbfname.assign(""); + + } else { + construct_db_file_name(dbfname); + tdbname = dbfname.c_str(); + tdb = open_db(dbp->get_env(), tdbname, dbtype, oflags, sflags); + } + + return tdb; +} + +int db_container::construct_db_file_name(string &filename) const +{ + db_threadid_t tid; + db_timespec ts; + int len; + char name[64]; + + __os_gettime(NULL, &ts, 1); + __os_id(NULL, NULL, &tid); + + // avoid name clash + len = _snprintf(name, 64, "tmpdb_db_map_%lu_%d_%u.db", + (u_long)((uintptr_t)tid + ts.tv_nsec), rand(), g_db_file_suffix_++); + filename = name; + + return 0; +} + +void db_container::set_auto_commit(Db *db) +{ + u_int32_t envof, envf, dbf; + + if (db == NULL || dbenv_ == NULL) { + auto_commit_ = false; + return; + } + + dbenv_->get_open_flags(&envof); + if ((envof & DB_INIT_TXN) == 0) { + this->auto_commit_ = false; + } else { + dbenv_->get_flags(&envf); + db->get_flags(&dbf); + if (((envf & DB_AUTO_COMMIT) != 0) || + ((dbf & DB_AUTO_COMMIT) != 0)) + this->auto_commit_ = true; + else + this->auto_commit_ = false; + } +} + +void db_container::set_db_handle(Db *dbp, DbEnv *newenv) +{ + const char *errmsg; + + if ((errmsg = verify_config(dbp, newenv)) != NULL) { + THROW(InvalidArgumentException, ("Db*", errmsg)); + + } + + pdb_ = dbp; + if (newenv) + dbenv_ = newenv; + +} + +void db_container::verify_db_handles(const db_container &cntnr) const +{ + Db *pdb2 = cntnr.get_db_handle(); + const char *home = NULL, *home2 = NULL, *dbf = NULL, *dbn = NULL, + *dbf2 = NULL, *dbn2 = NULL; + int ret = 0; + u_int32_t flags = 0, flags2 = 0; + bool same_dbfile, same_dbname, anonymous_inmemdbs; + // Check the two database handles do not refer to the same database. + // If they don't point to two anonymous databases at the same time, + // then two identical file names and two identical database names + // mean the two databases are the same. + assert(this->pdb_ != pdb2); + if (pdb_ == NULL) + return; + + BDBOP(pdb_->get_dbname(&dbf, &dbn), ret); + BDBOP(pdb2->get_dbname(&dbf2, &dbn2), ret); + + anonymous_inmemdbs = (dbf == NULL && dbf2 == NULL && + dbn == NULL && dbn2 == NULL); + + same_dbfile = (dbf != NULL && dbf2 != NULL && (strcmp(dbf, dbf2) == 0)) + || (dbf == NULL && dbf2 == NULL); + + same_dbname = (dbn == NULL && dbn2 == NULL) || + (dbn != NULL && dbn2 != NULL && strcmp(dbn, dbn2) == 0); + + assert((!(anonymous_inmemdbs) && same_dbfile && same_dbname) == false); + + // If any one of the two environments are transactional, both of them + // should be opened in the same transactional environment. + DbEnv *penv2 = cntnr.get_db_env_handle(); + if (dbenv_ != penv2 ){ + BDBOP(this->dbenv_->get_open_flags(&flags), ret); + BDBOP(penv2->get_open_flags(&flags2), ret); + + if ((flags & DB_INIT_TXN) || (flags2 & DB_INIT_TXN)) { + BDBOP(dbenv_->get_home(&home), ret); + BDBOP(penv2->get_home(&home), ret); + assert(home != NULL && home2 != NULL && + strcmp(home, home2) == 0); + } + } +} + +bool operator==(const Dbt&d1, const Dbt&d2) +{ + if (d1.get_size() != d2.get_size()) + return false; + + return memcmp(d1.get_data(), d2.get_data(), + d2.get_size()) == 0; +} + +bool operator==(const DBT&d1, const DBT&d2) +{ + if (d1.size != d2.size) + return false; + return memcmp(d1.data, d2.data, d2.size) == 0; +} + +void close_all_dbs() +{ + ResourceManager::instance()->close_all_dbs(); +} + +void close_db(Db *pdb) +{ + ResourceManager::instance()->close_db(pdb); +} + +DbTxn* begin_txn(u_int32_t flags, DbEnv*env) +{ + return ResourceManager::instance()->begin_txn(flags, env, 1); +} + +void commit_txn(DbEnv *env, u_int32_t flags) +{ + ResourceManager::instance()->commit_txn(env, flags); +} + +void commit_txn(DbEnv *env, DbTxn *txn, u_int32_t flags) +{ + ResourceManager::instance()->commit_txn(env, txn, flags); +} + +void abort_txn(DbEnv *env) +{ + ResourceManager::instance()->abort_txn(env); +} + +void abort_txn(DbEnv *env, DbTxn *txn) +{ + ResourceManager::instance()->abort_txn(env, txn); +} + +DbTxn* current_txn(DbEnv *env) +{ + return ResourceManager::instance()->current_txn(env); +} + +DbTxn* set_current_txn_handle(DbEnv *env, DbTxn *newtxn) +{ + return ResourceManager::instance()-> + set_current_txn_handle(env, newtxn); +} + +void register_db(Db *pdb1) +{ + ResourceManager::instance()->register_db(pdb1); +} + +void register_db_env(DbEnv *env1) +{ + ResourceManager::instance()->register_db_env(env1); +} + +Db* open_db (DbEnv *penv, const char *filename, DBTYPE dbtype, + u_int32_t oflags, u_int32_t set_flags, int mode, + DbTxn *txn, u_int32_t cflags, const char *dbname) +{ + return ResourceManager::instance()->open_db( + penv, filename, dbtype, oflags, set_flags, mode, txn, + cflags, dbname); +} + +DbEnv* open_env(const char *env_home, u_int32_t set_flags, + u_int32_t oflags, u_int32_t cachesize, int mode, u_int32_t cflags) +{ + return ResourceManager::instance()->open_env( + env_home, set_flags, oflags, cachesize, mode, cflags); +} + +void close_db_env(DbEnv *pdbenv) +{ + + ResourceManager::instance()->close_db_env(pdbenv); +} + +void close_all_db_envs() +{ + ResourceManager::instance()->close_all_db_envs(); +} + +size_t close_db_cursors(Db *dbp1) +{ + return ResourceManager::instance()->close_db_cursors(dbp1); +} + +db_mutex_t alloc_mutex() +{ + int ret; + db_mutex_t mtx; + + BDBOP2(ResourceManager::instance()->get_mutex_env()->mutex_alloc( + DB_MUTEX_PROCESS_ONLY, &mtx), ret, ResourceManager:: + instance()->get_mutex_env()->mutex_free(mtx)); + return mtx; +} + +int lock_mutex(db_mutex_t mtx) +{ + int ret; + + BDBOP2(ResourceManager::instance()->global_lock(mtx), ret, + ResourceManager:: + instance()->get_mutex_env()->mutex_free(mtx)); + return 0; +} + +int unlock_mutex(db_mutex_t mtx) +{ + int ret; + + BDBOP2(ResourceManager::instance()->global_unlock(mtx), ret, + ResourceManager:: + instance()->get_mutex_env()->mutex_free(mtx)); + return 0; +} + +void free_mutex(db_mutex_t mtx) +{ + ResourceManager::instance()->get_mutex_env()->mutex_free(mtx); +} + +void dbstl_startup() +{ + ResourceManager::instance()->global_startup(); +} + +void dbstl_exit() +{ + ResourceManager::instance()->global_exit(); +} + +// Internally used only. +void throw_bdb_exception(const char *caller, int error) +{ + switch (error) { + case DB_LOCK_DEADLOCK: + { + DbDeadlockException dl_except(caller); + throw dl_except; + } + case DB_LOCK_NOTGRANTED: + { + DbLockNotGrantedException lng_except(caller); + throw lng_except; + } + case DB_REP_HANDLE_DEAD: + { + DbRepHandleDeadException hd_except(caller); + throw hd_except; + } + case DB_RUNRECOVERY: + { + DbRunRecoveryException rr_except(caller); + throw rr_except; + } + default: + { + DbException except(caller, error); + throw except; + } + } +} + +void register_global_object(DbstlGlobalInnerObject *gio) +{ + ResourceManager::instance()->register_global_object(gio); +} + +u_int32_t hash_default(Db * /* dbp */, const void *key, u_int32_t len) +{ + const u_int8_t *k, *e; + u_int32_t h; + + k = (const u_int8_t *)key; + e = k + len; + for (h = 0; k < e; ++k) { + h *= 16777619; + h ^= *k; + } + return (h); +} + +bool DbstlMultipleDataIterator::next(Dbt &data) +{ + if (*p_ == (u_int32_t)-1) { + data.set_data(0); + data.set_size(0); + p_ = 0; + } else { + data.set_data(data_ + *p_--); + data.set_size(*p_--); + if (data.get_size() == 0 && data.get_data() == data_) + data.set_data(0); + } + return (p_ != 0); +} + +bool DbstlMultipleKeyDataIterator::next(Dbt &key, Dbt &data) +{ + if (*p_ == (u_int32_t)-1) { + key.set_data(0); + key.set_size(0); + data.set_data(0); + data.set_size(0); + p_ = 0; + } else { + key.set_data(data_ + *p_); + p_--; + key.set_size(*p_); + p_--; + data.set_data(data_ + *p_); + p_--; + data.set_size(*p_); + p_--; + } + return (p_ != 0); +} + +bool DbstlMultipleRecnoDataIterator::next(db_recno_t &recno, Dbt &data) +{ + if (*p_ == (u_int32_t)0) { + recno = 0; + data.set_data(0); + data.set_size(0); + p_ = 0; + } else { + recno = *p_--; + data.set_data(data_ + *p_--); + data.set_size(*p_--); + } + return (p_ != 0); +} + +END_NS + diff --git a/stl/dbstl_container.h b/stl/dbstl_container.h new file mode 100644 index 0000000..279fe40 --- /dev/null +++ b/stl/dbstl_container.h @@ -0,0 +1,582 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#ifndef _DB_STL_CONTAINER_H__ +#define _DB_STL_CONTAINER_H__ + +#include "dbstl_common.h" +#include "dbstl_resource_manager.h" +#include <assert.h> + +START_NS(dbstl) + +class ResourceManager; + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +// +// db_container class definition +// +// This class's begin_txn, commit/abort_txn is used to wrap each DB related +// operation inside dbstl. When auto commit is enabled, each operation will +// be auto committed before returning, and aborted when an exception is thrown. +// +// It also contains members to hold all needed parameters and flags for +// transaction and cursor related calls. +// +// Container classes will inherit from this class. Each container will enclose +// every db related operation with db_container::begin_txn and +// db_container::commit_txn, and if exception is not caught, abort_txn() +// should be called. + +/** \defgroup dbstl_containers dbstl container classes +A dbstl container is very much like a C++ STL container. It stores a +collection of data items, or key/data pairs. +Each container is backed by a Berkeley DB database created in an explicit +database environment or an internal private environment; And the database +itself can be created explicitly with all kinds of configurations, or +by dbstl internally. For each type of container, some specific type of +database and/or configurations must be used or specified to the database +and its environment. dbstl will check the database and environment conform +to the requirement. When users don't have a chance to specify a container's +backing database and environment, like in copy constructors, dbstl will +create proper databases and/or environment for it. There are two helper +functions to make it easier to create/open an environment or database, they +are dbstl::open_db() and dbstl::open_env(); +\sa dbstl::open_db() dbstl::open_env() db_vector db_map db_multimap db_set +db_multiset +*/ + +/** \ingroup dbstl_containers +@{ +This class is the base class for all db container classes, you don't directly +use this class, but all container classes inherit from this class, so you need +to know the methods that can be accessed via concrete container classes. +This class is also used to support auto commit transactions. Autocommit is +enabled when DB_AUTO_COMMIT is set to the database or database environment +handle and the environment is transactional. + +Inside dbstl, there are transactions begun and committed/aborted if the backing +database and/or environment requires auto commit, and there are cursors +opened internally, and you can set the flags used by the transaction and cursor +functions via set functions of this class. + +All dbstl containers are fully multi-threaded, you should not need any +synchronization to use them in the correct way, but this class is not thread +safe, access to its members are not proctected by any mutex because the data +members of this class are supposed to be set before they are used, +and remain read only afterwards. If this is not the case, you must synchronize +the access. +*/ +class _exported db_container +{ +private: + // By default these flags are 0, users should configure these values + // on container initialization. + // + u_int32_t txn_begin_flags_, commit_flags_; + mutable u_int32_t cursor_oflags_; + + // Berkeley DB database handle for each container. Subclasses do not + // need to define it. + // + Db *pdb_; + + // Berkeley DB environment handle in which the db handle is opened. + DbEnv *dbenv_; + + // db_map_iterator<> needs to know whether the container is a + // db_(multi)set or not + // + bool is_set_; + + // Determined automatically, by inspecting users the Berkeley DB + // database and environment configuration options. + // + bool auto_commit_; + + // If exisiting random temporary database name generation mechanism is + // still causing name clashes, users can set this global suffix number + // which will be append to each temporary database file name and by + // default it is 0. there is a dbstl::set_global_dbfile_suffix_number + // to do so. + static u_int32_t g_db_file_suffix_; + friend void set_global_dbfile_suffix_number(u_int32_t); +protected: + + // Does not clone or copy data from database parameter. + // Return the db file name back in the dbfname parameter. + // We construct a default database name the user can rename it using + // either DbEnv::dbrename or Db::rename. + // + Db* clone_db_config(Db *dbp, std::string &dbfname); + Db* clone_db_config(Db *dbp); + + // Store the name into name parameter whose length is n. Return -1 if + // not enough space. + int construct_db_file_name(std::string &filename) const; + + // Check that this container and cntnr are backed by different databases + // and if any one of them is using transactions, both should be in the + // same transactional environment. + // Called by all deriving classes' methods + // which have a container parameter. + void verify_db_handles(const db_container &cntnr) const; + + void open_db_handles(Db *&pdb, DbEnv *&penv, DBTYPE dbtype, + u_int32_t oflags, u_int32_t sflags); + + inline void set_is_set(bool b) + { + is_set_ = b; + } + + inline bool is_set() const + { + return is_set_; + } + + // Database and environment handles and autocommit and is_set_ are + // not assigned, because they are the nature of my own db, not + // that of dbctnr. + inline const db_container &operator=(const db_container & dbctnr) + { + ASSIGNMENT_PREDCOND(dbctnr) + txn_begin_flags_ = dbctnr.txn_begin_flags_; + commit_flags_ = dbctnr.commit_flags_; + cursor_oflags_ = dbctnr.cursor_oflags_; + return dbctnr; + } + +public: + /// Default constructor. + db_container(); + + /// Copy constructor. + /// The new container will be backed by another database within the + /// same environment unless dbctnr's backing database is in its own + /// internal private environment. The name of the database is coined + /// based on current time and thread id and some random number. If + /// this is still causing naming clashes, you can set a suffix number + /// via "set_global_dbfile_suffix_number" function; And following db + /// file will suffix this number in the file name for additional + /// randomness. And the suffix will be incremented after each such use. + /// You can change the file name via DbEnv::rename. + /// If dbctnr is using an anonymous database, the newly constructed + /// container will also use an anonymous one. + /// \param dbctnr The container to initialize this container. + db_container(const db_container &dbctnr); + + /** + This constructor is not directly called by the user, but invoked by + constructors of concrete container classes. The statement about the + parameters applies to constructors of all container classes. + \param dbp Database handle. dbp is supposed to be opened inside envp. + Each dbstl container is backed by a Berkeley DB database, so dbstl + will create an internal anonymous database if dbp is NULL. + \param envp Environment handle. And envp can also be NULL, meaning the + dbp handle may be created in its internal private environment. + */ + db_container(Db *dbp, DbEnv *envp); + + /// The backing database is not closed in this function. It is closed + /// when current thread exits and the database is no longer referenced + /// by any other container instances in this process. + /// In order to make the reference counting work alright, you must call + /// register_db(Db*) and register_db_env(DbEnv*) correctly. + /// \sa register_db(Db*) register_db_env(DbEnv*) + virtual ~db_container(){} + + /// \name Get and set functions for data members. + /// Note that these functions are not thread safe, because all data + /// members of db_container are supposed to be set on container + /// construction and initialization, and remain read only afterwards. + //@{ + /// Get the backing database's open flags. + /// \return The backing database's open flags. + inline u_int32_t get_db_open_flags() const + { + u_int32_t oflags; + pdb_->get_open_flags(&oflags); + return oflags; + } + + /// Get the backing database's flags that are set via Db::set_flags() + /// function. + /// \return Flags set to this container's database handle. + inline u_int32_t get_db_set_flags() const + { + u_int32_t oflags; + pdb_->get_flags(&oflags); + return oflags; + } + + /// Get the backing database's handle. + /// \return The backing database handle of this container. + inline Db* get_db_handle() const + { + return pdb_; + } + + /// Get the backing database environment's handle. + /// \return The backing database environment handle of this container. + inline DbEnv* get_db_env_handle() const + { + return dbenv_; + } + + /** + Set the underlying database's handle, and optionally environment + handle if the environment has also changed. That is, users can change + the container object's underlying database while the object is alive. + dbstl will verify that the handles set conforms to the concrete + container's requirement to Berkeley DB database/environment handles. + \param dbp The database handle to set. + \param newenv The database environment handle to set. + */ + void set_db_handle(Db *dbp, DbEnv *newenv = NULL); + + /** Set the flags required by the Berkeley DB functions + DbEnv::txn_begin(), DbTxn::commit() and DbEnv::cursor(). These flags + will be set to this container's auto commit member functions when + auto commit transaction is used, except that cursor_oflags is set to + the Dbc::cursor when creating an iterator for this container. + By default the three flags are all zero. + You can also set the values of the flags individually by using the + appropriate set functions in this class. The corresponding get + functions return the flags actually used. + \param txn_begin_flags Flags to be set to DbEnv::txn_begin(). + \param commit_flags Flags to be set to DbTxn::commit(). + \param cursor_open_flags Flags to be set to Db::cursor(). + */ + inline void set_all_flags(u_int32_t txn_begin_flags, + u_int32_t commit_flags, u_int32_t cursor_open_flags) + { + this->txn_begin_flags_ = txn_begin_flags; + this->commit_flags_ = commit_flags; + this->cursor_oflags_ = cursor_open_flags; + } + + /// Set flag of DbEnv::txn_begin() call. + /// \param flag Flags to be set to DbEnv::txn_begin(). + inline void set_txn_begin_flags(u_int32_t flag ) + { + txn_begin_flags_ = flag; + } + + /// Get flag of DbEnv::txn_begin() call. + /// \return Flags to be set to DbEnv::txn_begin(). + inline u_int32_t get_txn_begin_flags() const + { + return txn_begin_flags_; + } + + /// Set flag of DbTxn::commit() call. + /// \param flag Flags to be set to DbTxn::commit(). + inline void set_commit_flags(u_int32_t flag) + { + commit_flags_ = flag; + } + + /// Get flag of DbTxn::commit() call. + /// \return Flags to be set to DbTxn::commit(). + inline u_int32_t get_commit_flags() const + { + return commit_flags_; + } + + /// Get flag of Db::cursor() call. + /// \return Flags to be set to Db::cursor(). + inline u_int32_t get_cursor_open_flags() const + { + return cursor_oflags_; + } + + /// Set flag of Db::cursor() call. + /// \param flag Flags to be set to Db::cursor(). + inline void set_cursor_open_flags(u_int32_t flag) + { + cursor_oflags_ = flag; + } + //@} // getter_setters + +protected: + void init_members(); + void init_members(Db *pdb, DbEnv *env); + void init_members(const db_container&cnt); + // Called internally by concrete container constructors. Verification + // is done by the specialized constructors + void set_db_handle_int(Db *dbp, DbEnv *envp) + { + this->pdb_ = dbp; + this->dbenv_ = envp; + } + + // Child classes override this function to check the db and environment + // handles are correctly configured. + virtual const char* verify_config(Db* pdb, DbEnv* penv) const + { + if (pdb != NULL && ((pdb->get_create_flags() & + DB_CXX_NO_EXCEPTIONS) == 0)) + return +"Db and DbEnv object must be constructed with DB_CXX_NO_EXCEPTIONS flag set."; + + if (penv != NULL && ((penv->get_create_flags() & + DB_CXX_NO_EXCEPTIONS) == 0)) + return +"Db and DbEnv object must be constructed with DB_CXX_NO_EXCEPTIONS flag set."; + return NULL; + } + + // Use db and dbenv_ to determine whether to enable autocommit. If + // DB_AUTOCOMMIT is set on dbenv_ or db and dbenv_ is transactional, + // that db should be autocommit. + // + void set_auto_commit(Db* db); + + // Begin a transaction. Used to make a container's db related + // operations auto commit when the operation completes and abort + // when the operation fails. If there is already a transaction for this + // container's environment, then that transaction is used. If the + // transaction was created by DB STL, its reference count is + // incremented (user created and external transactions are not + // reference counted because they can be nested.). + inline DbTxn* begin_txn() const + { + DbTxn *txn = NULL; + + if (this->auto_commit_) { + txn = ResourceManager::instance()->begin_txn( + this->txn_begin_flags_, dbenv_, 0); + } + return txn; + } + + inline void commit_txn() const + { + if (this->auto_commit_) { + ResourceManager::instance()-> + commit_txn(pdb_->get_env(), this->commit_flags_); + } + } + + inline void abort_txn() const + { + if (this->auto_commit_) + ResourceManager::instance()-> + abort_txn(pdb_->get_env()); + } + + inline DbTxn *current_txn() const + { + return ResourceManager::instance()-> + current_txn(pdb_->get_env()); + } +}; // db_container +/** @} */ // dbstl_containers + +/// \addtogroup dbstl_helper_classes +//@{ +/// Bulk retrieval configuration helper class. Used by the begin() function of +/// a container. +class _exported BulkRetrievalOption +{ +public: + enum Option {BulkRetrieval, NoBulkRetrieval}; + +protected: + Option bulk_retrieve; + u_int32_t bulk_buf_sz_; + + inline BulkRetrievalOption() + { + bulk_retrieve = NoBulkRetrieval; + bulk_buf_sz_ = DBSTL_BULK_BUF_SIZE; + } + +public: + inline BulkRetrievalOption(Option bulk_retrieve1, + u_int32_t bulk_buf_sz = DBSTL_BULK_BUF_SIZE) + { + this->bulk_retrieve = bulk_retrieve1; + this->bulk_buf_sz_ = bulk_buf_sz; + } + + // The following two static members should best be const, but this is + // impossible because in C++ this definition is a function declaration: + // const static BulkRetrievalOption BulkRetrieval(BulkRetrieval); + // i.e. const static members can only be inited with default copy + // constructor. + // + // Static data members can't be compiled into a .lib for a dll on + // Windows, code using the static members will have link error--- + // unresolved symbols, so we have to use a static function here. + /// This function indicates that you need a bulk retrieval iterator, + /// and it can be also used to optionally set the bulk read buffer size. + inline static BulkRetrievalOption bulk_retrieval( + u_int32_t bulk_buf_sz = DBSTL_BULK_BUF_SIZE) + { + return BulkRetrievalOption(BulkRetrieval, bulk_buf_sz); + } + + /// This function indicates that you do not need a bulk retrieval + /// iterator. + inline static BulkRetrievalOption no_bulk_retrieval() + { + return BulkRetrievalOption(NoBulkRetrieval, 0); + } + + /// Equality comparison. + inline bool operator==(const BulkRetrievalOption& bro) const + { + return bulk_retrieve == bro.bulk_retrieve; + } + + /// Assignment operator. + inline void operator=(BulkRetrievalOption::Option opt) + { + bulk_retrieve = opt; + } + + /// Return the buffer size set to this object. + inline u_int32_t bulk_buf_size() + { + return bulk_buf_sz_; + } +}; +//@} + +/// \addtogroup dbstl_helper_classes +//@{ +/// Read-modify-write cursor configuration helper class. Used by each begin() +/// function of all containers. +class _exported ReadModifyWriteOption +{ +protected: + enum Option{ReadModifyWrite, NoReadModifyWrite}; + Option rmw; + + inline ReadModifyWriteOption(Option rmw1) + { + this->rmw = rmw1; + } + + inline ReadModifyWriteOption() + { + rmw = NoReadModifyWrite; + } + +public: + /// Assignment operator. + inline void operator=(ReadModifyWriteOption::Option rmw1) + { + this->rmw = rmw1; + } + + /// Equality comparison. + inline bool operator==(const ReadModifyWriteOption &rmw1) const + { + return this->rmw == rmw1.rmw; + } + + /// Call this function to tell the container's begin() function that + /// you need a read-modify-write iterator. + inline static ReadModifyWriteOption read_modify_write() + { + return ReadModifyWriteOption(ReadModifyWrite); + } + + /// Call this function to tell the container's begin() function that + /// you do not need a read-modify-write iterator. This is the default + /// value for the parameter of any container's begin() function. + inline static ReadModifyWriteOption no_read_modify_write() + { + return ReadModifyWriteOption(NoReadModifyWrite); + } + +}; +//@} // dbstl_helper_classes + +// The classes in the Berkeley DB C++ API do not expose data and p_ member. +// Extend the class to provide this functionality, rather than altering the +// internal implementations. +// +class _exported DbstlMultipleIterator : protected DbMultipleIterator +{ +protected: + DbstlMultipleIterator(const Dbt &dbt) : DbMultipleIterator(dbt) {} +public: + u_int32_t get_pointer() + { + u_int32_t off; + off = (u_int32_t)((u_int8_t*)p_ - data_); + return off; + } + + inline void set_pointer(u_int32_t offset) + { + p_ = (u_int32_t*)(data_ + offset); + } + +}; + +class _exported DbstlMultipleKeyDataIterator : public DbstlMultipleIterator +{ +public: + DbstlMultipleKeyDataIterator(const Dbt &dbt) + : DbstlMultipleIterator(dbt) {} + bool next(Dbt &key, Dbt &data); +}; + +class _exported DbstlMultipleRecnoDataIterator : public DbstlMultipleIterator +{ +public: + DbstlMultipleRecnoDataIterator(const Dbt &dbt) + : DbstlMultipleIterator(dbt) {} + bool next(db_recno_t &recno, Dbt &data); +}; + +class _exported DbstlMultipleDataIterator : public DbstlMultipleIterator +{ +public: + DbstlMultipleDataIterator(const Dbt &dbt) + : DbstlMultipleIterator(dbt) {} + bool next(Dbt &data); +}; + +// These classes are used to give data values meaningful default +// initializations. They are only necessary for types that do not have a +// reasonable default constructor - explicitly char *, wchar_t * and T*. +// The string types don't have a reasonable default initializer since we store +// the underlying content, not a pointer. +// So we fully instantiate types for T* and const T* and set the pointers to +// NULL and leave other types intact. +template <Typename T> +class DbstlInitializeDefault +{ +public: + DbstlInitializeDefault(T&){} +}; + +template <Typename T> +class DbstlInitializeDefault<T *> +{ +public: + DbstlInitializeDefault(T *& t){t = NULL;} +}; + +template <Typename T> +class DbstlInitializeDefault<const T *> +{ +public: + DbstlInitializeDefault(const T *& t){t = NULL;} +}; + +END_NS + +#endif //_DB_STL_CONTAINER_H__ diff --git a/stl/dbstl_dbc.h b/stl/dbstl_dbc.h new file mode 100644 index 0000000..bcf6695 --- /dev/null +++ b/stl/dbstl_dbc.h @@ -0,0 +1,1327 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#ifndef _DB_STL_DBC_H +#define _DB_STL_DBC_H + +#include <set> +#include <errno.h> + +#include "dbstl_common.h" +#include "dbstl_dbt.h" +#include "dbstl_exception.h" +#include "dbstl_container.h" +#include "dbstl_resource_manager.h" + +START_NS(dbstl) + +// Forward declarations. +class db_container; +class DbCursorBase; +template<Typename data_dt> +class RandDbCursor; +class DbstlMultipleKeyDataIterator; +class DbstlMultipleRecnoDataIterator; +using std::set; + +///////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////// +// +// LazyDupCursor class template definition. +// +// This class allows us to make a shallow copy on construction. When the +// cursor pointer is first dereferenced a deep copy is made. +// +// The allowed type for BaseType is DbCursor<> and RandDbCursor<> +// The expected usage of this class is: +// 1. Create an iterator in container::begin(), the iterator::pcsr.csr_ptr_ +// points to an object, thus no need to duplicate. +// 2. The iterator is created with default argument, thus the +// iterator::pcsr.csr_ptr_ and dup_src_ is NULL, and this iterator is +// copied using copy constructor for may be many times, but until the +// cursor is really used, no cursor is duplicated. +// +// There is an informing mechanism between an instance of this class and +// its dup_src_ cursor: when that cursor is about to change state, it will +// inform all registered LazyDupCursor "listeners" of the change, so that +// they will duplicate from the cursor before the change, because that +// is the expected cursor state for the listeners. + +template <Typename BaseType> +class LazyDupCursor +{ + // dup_src_ is used by this class internally to duplicate another + // cursor and set to csr_ptr_, and it is assigned in the copy + // constructor from another LazyDupCursor object's csr_ptr_; csr_ptr_ + // is the acutual pointer that is used to perform cursor operations. + // + BaseType *csr_ptr_, *dup_src_; + typedef LazyDupCursor<BaseType> self; + +public: + //////////////////////////////////////////////////////////////////// + // + // Begin public constructors and destructor. + // + inline LazyDupCursor() + { + csr_ptr_ = NULL; + dup_src_ = NULL; + } + + // Used in all iterator types' constructors, dbcptr is created + // solely for this object, and the cursor is not yet opened, so we + // simply assign it to csr_ptr_. + explicit inline LazyDupCursor(BaseType *dbcptr) + { + csr_ptr_ = dbcptr; + // Already have pointer, do not need to duplicate. + dup_src_ = NULL; + } + + // Do not copy to csr_ptr_, shallow copy from dp2.csr_ptr_. + LazyDupCursor(const self& dp2) + { + csr_ptr_ = NULL; + if (dp2.csr_ptr_) + dup_src_ = dp2.csr_ptr_; + else + dup_src_ = dp2.dup_src_; + if (dup_src_) + dup_src_->add_dupper(this); + } + + ~LazyDupCursor() + { + // Not duplicated yet, remove from dup_src_. + if (csr_ptr_ == NULL && dup_src_ != NULL) + dup_src_->erase_dupper(this); + if (csr_ptr_) + delete csr_ptr_;// Delete the cursor. + } + + //////////////////////////////////////////////////////////////////// + + // Deep copy. + inline const self& operator=(const self &dp2) + { + BaseType *dcb; + + dcb = dp2.csr_ptr_ ? dp2.csr_ptr_ : dp2.dup_src_; + this->operator=(dcb); + + return dp2; + } + + // Deep copy. + inline BaseType *operator=(BaseType *dcb) + { + + if (csr_ptr_) { + // Only dup_src_ will inform this, not csr_ptr_. + delete csr_ptr_; + csr_ptr_ = NULL; + } + + if (dcb) + csr_ptr_ = new BaseType(*dcb); + if (dup_src_ != NULL) { + dup_src_->erase_dupper(this); + dup_src_ = NULL; + } + + return dcb; + } + + void set_cursor(BaseType *dbc) + { + assert(dbc != NULL); + if (csr_ptr_) { + // Only dup_src_ will inform this, not csr_ptr_. + delete csr_ptr_; + csr_ptr_ = NULL; + } + + csr_ptr_ = dbc; + if (dup_src_ != NULL) { + dup_src_->erase_dupper(this); + dup_src_ = NULL; + } + } + + // If dup_src_ is informing this object, pass false parameter. + inline BaseType* duplicate(bool erase_dupper = true) + { + assert(dup_src_ != NULL); + if (csr_ptr_) { + // Only dup_src_ will inform this, not csr_ptr_. + delete csr_ptr_; + csr_ptr_ = NULL; + } + csr_ptr_ = new BaseType(*dup_src_); + if (erase_dupper) + dup_src_->erase_dupper(this); + dup_src_ = NULL; + return csr_ptr_; + } + + inline BaseType* operator->() + { + if (csr_ptr_) + return csr_ptr_; + + return duplicate(); + } + + inline operator bool() + { + return csr_ptr_ != NULL; + } + + inline bool operator!() + { + return !csr_ptr_; + } + + inline bool operator==(void *p) + { + return csr_ptr_ == p; + } + + inline BaseType* base_ptr(){ + if (csr_ptr_) + return csr_ptr_; + return duplicate(); + } +}; + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// DbCursorBase class definition. +// +// DbCursorBase is the base class for DbCursor<> class template, this class +// wraps the Berkeley DB cursor, in order for the ResourceManager to close +// the Berkeley DB cursor and set the pointer to null. +// If we don't set the cursor to NULL, the handle could become valid again, +// since Berkeley DB recycles handles. DB STL would then try to use the same +// handle across different instances, which is not supported. +// +// In ResourceManager, whenver a cursor is opened, it stores the +// DbCursorBase* pointer, so that when need to close the cursor, it calls +// DbCursorBase::close() function. +// +class DbCursorBase +{ +protected: + Dbc *csr_; + DbTxn *owner_txn_; + Db *owner_db_; + int csr_status_; + +public: + enum DbcGetSkipOptions{SKIP_KEY, SKIP_DATA, SKIP_NONE}; + inline DbTxn *get_owner_txn() const { return owner_txn_;} + inline void set_owner_txn(DbTxn *otxn) { owner_txn_ = otxn;} + + inline Db *get_owner_db() const { return owner_db_;} + inline void set_owner_db(Db *odb) { owner_db_ = odb;} + + inline Dbc *get_cursor() const { return csr_;} + inline Dbc *&get_cursor_reference() { return csr_;} + inline void set_cursor(Dbc*csr1) + { + if (csr_) + ResourceManager::instance()->remove_cursor(this); + csr_ = csr1; + } + + inline int close() + { + int ret = 0; + + if (csr_ != NULL && (((DBC *)csr_)->flags & DBC_ACTIVE) != 0) { + ret = csr_->close(); + csr_ = NULL; + } + return ret; + } + + DbCursorBase(){ + owner_txn_ = NULL; + owner_db_ = NULL; + csr_ = NULL; + csr_status_ = 0; + } + + DbCursorBase(const DbCursorBase &csrbase) + { + this->operator=(csrbase); + } + + const DbCursorBase &operator=(const DbCursorBase &csrbase) + { + owner_txn_ = csrbase.owner_txn_; + owner_db_ = csrbase.owner_db_; + csr_ = NULL; // Need to call DbCursor<>::dup to duplicate. + csr_status_ = 0; + return csrbase; + } + + virtual ~DbCursorBase() + { + close(); + } +}; // DbCursorBase + +//////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////// +// +// DbCursor class template definition +// +// DbCursor is the connection between Berkeley DB and dbstl container classes +// it is the wrapper class for Dbc* cursor of Berkeley Db, to be used for +// iterator classes of Berkeley DB backed STL container classes. +// Requirement: +// 1. Deep copy using Dbc->dup. +// 2. Dbc*cursor management via ResourceManager class. +// 3. Provide methods to do increment, decrement and advance operations, +// advance is only available for random access iterator from DB_RECNO +// containers. +// + +template<typename key_dt, typename data_dt> +class DbCursor : public DbCursorBase{ +protected: + // Lazy duplication support: store the LazyDupCursor objects which + // will duplicate from this cursor. + typedef LazyDupCursor<DbCursor<key_dt, data_dt> > dupper_t; + typedef LazyDupCursor<RandDbCursor<data_dt> > dupperr_t; + typedef set<LazyDupCursor<DbCursor<key_dt, data_dt> >* > dupset_t; + typedef set<LazyDupCursor<RandDbCursor<data_dt> >* > dupsetr_t; + + set<LazyDupCursor<DbCursor<key_dt, data_dt> >* > sduppers1_; + set<LazyDupCursor<RandDbCursor<data_dt> >* > sduppers2_; + + // We must use DB_DBT_USERMEM for Dbc::get and Db::get if they are + // used in multi-threaded application, so we use key_buf_ and + // data_buf_ data members for get operations, and initialize them + // to use user memory. + Dbt key_buf_, data_buf_; + + // Similar to Berkeley DB C++ API's classes, used to iterate through + // bulk retrieved key/data pairs. + DbstlMultipleKeyDataIterator *multi_itr_; + DbstlMultipleRecnoDataIterator *recno_itr_; + + // Whether to use bulk retrieval. If non-zero, do bulk retrieval, + // bulk buffer size is this member, otherwise not bulk read. + // By default this member is 0. + u_int32_t bulk_retrieval_; + // Whether to use DB_RMW flag in Dbc::get, by default false. + bool rmw_get_; + + // Whether to poll data from cursor's current position on every + // get_current_key/data call. + // Note that curr_key_/curr_data_ members are always maintained + // to contain current k/d value of the pair pointed to by csr_. + // If doing bulk retrieval, this flag is ignored, we will always + // read data from bulk buffer. + bool directdb_get_; + + // Inform LazyDupCursor objects registered in this object to do + // duplication because this cursor is to be changed. + // This function should be called in any function of + // DbCursor and RandDbCursor whenever the cursor is about to change + // state(move/close, etc). + inline void inform_duppers() + { + typename dupset_t::iterator i1; + typename dupsetr_t::iterator i2; + for (i1 = sduppers1_.begin(); i1 != sduppers1_.end(); i1++) + (*i1)->duplicate(false); + for (i2 = sduppers2_.begin(); i2 != sduppers2_.end(); i2++) + (*i2)->duplicate(false); + sduppers1_.clear(); + sduppers2_.clear(); + } + +public: + friend class DataItem; + + // Current key/data pair pointed by "csr_" Dbc*cursor. They are both + // maintained on cursor movement. If directdb_get_ is true, + // they are both refreshed on every get_current{[_key][_data]} call and + // the retrieved key/data pair is returned to user. + DataItem curr_key_; + DataItem curr_data_; + + typedef DbCursor<key_dt, data_dt> self; + + // This function is used by all iterators to do equals comparison. + // Random iterators will also use it to do less than/greater than + // comparisons. + // Internally, the page number difference or index difference is + // returned, so for btree and hash databases, if two cursors point to + // the same key/data pair, we will get 0 returned, meaning they are + // equal; if return value is not 0, it means no more than that they + // they are not equal. We can't assume any order information between + // the two cursors. For recno databases, we use the recno to do less + // than and greater than comparison. So we can get a reliable knowledge + // of the relative position of two iterators from the return value. + int compare(const self *csr2) const{ + int res, ret; + + BDBOP(((DBC *)csr_)->cmp((DBC *)csr_, (DBC *)csr2->csr_, + &res, 0), ret); + return res; + } + + //////////////////////////////////////////////////////////////////// + // + // Add and remove cursor change event listeners. + // + inline void add_dupper(dupper_t *dupper) + { + sduppers1_.insert(dupper); + } + + inline void add_dupper(dupperr_t *dupper) + { + sduppers2_.insert(dupper); + } + + inline void erase_dupper(dupper_t *dup1) + { + sduppers1_.erase(dup1); + } + + inline void erase_dupper(dupperr_t *dup1) + { + sduppers2_.erase(dup1); + } + + //////////////////////////////////////////////////////////////////// + +public: + + inline bool get_rmw() + { + return rmw_get_; + } + + bool set_rmw(bool rmw, DB_ENV *env = NULL ) + { + u_int32_t flag = 0; + DB_ENV *dbenv = NULL; + int ret; + + if (env) + dbenv = env; + else + dbenv = ((DBC*)csr_)->dbenv; + BDBOP(dbenv->get_open_flags(dbenv, &flag), ret); + + // DB_RMW flag requires locking subsystem started. + if (rmw && ((flag & DB_INIT_LOCK) || (flag & DB_INIT_CDB) || + (flag & DB_INIT_TXN))) + rmw_get_ = true; + else + rmw_get_ = false; + return rmw_get_; + } + + // Modify bulk buffer size. Bulk read is enabled when creating an + // iterator, so users later can only modify the bulk buffer size + // to another value, but can't enable/disable bulk read while an + // iterator is already alive. + // Returns true if succeeded, false otherwise. + inline bool set_bulk_buffer(u_int32_t sz) + { + if (bulk_retrieval_ && sz) { + normalize_bulk_bufsize(sz); + bulk_retrieval_ = sz; + return true; + } + + return false; + + } + + inline u_int32_t get_bulk_bufsize() + { + return bulk_retrieval_; + } + + inline void enlarge_dbt(Dbt &d, u_int32_t sz) + { + void *p; + + p = DbstlReAlloc(d.get_data(), sz); + dbstl_assert(p != NULL); + d.set_ulen(sz); + d.set_data(p); + d.set_size(sz); + } + // Move forward or backward, often by 1 key/data pair, we can use + // different flags for Dbc::get function. Then update the key/data + // pair and csr_status_ members. + // + int increment(int flag) + { + int ret = 0; + Dbt &k = key_buf_, &d = data_buf_; + u_int32_t sz, getflags = 0, bulk_bufsz; + + if (csr_ == NULL) + return INVALID_ITERATOR_CURSOR; + curr_key_.reset(); + curr_data_.reset(); + inform_duppers(); + + // Berkeley DB cursor flags are not bitwise set, so we can't + // use bit operations here. + // + if (this->bulk_retrieval_ != 0) + switch (flag) { + case DB_PREV: + case DB_PREV_DUP: + case DB_PREV_NODUP: + case DB_LAST: + case DB_JOIN_ITEM: + case DB_GET_RECNO: + case DB_SET_RECNO: + break; + default: + getflags |= DB_MULTIPLE_KEY; + if (data_buf_.get_ulen() != bulk_retrieval_) + enlarge_dbt(data_buf_, bulk_retrieval_); + break; + } + + if (this->rmw_get_) + getflags |= DB_RMW; + + // Do not use BDBOP or BDBOP2 here because it is likely + // that an iteration will step onto end() position. +retry: ret = csr_->get(&k, &d, flag | getflags); + if (ret == 0) { + if (bulk_retrieval_ && (getflags & DB_MULTIPLE_KEY)) { + // A new retrieval, so both multi_itr_ and + // recno_itr_ must be NULL. + if (((DBC*)csr_)->dbtype == DB_RECNO) { + if (recno_itr_) { + delete recno_itr_; + recno_itr_ = NULL; + } + recno_itr_ = + new DbstlMultipleRecnoDataIterator(d); + } else { + if (multi_itr_) { + delete multi_itr_; + multi_itr_ = NULL; + } + multi_itr_ = new + DbstlMultipleKeyDataIterator(d); + } + } else { + // Non bulk retrieval succeeded. + curr_key_.set_dbt(k, false); + curr_data_.set_dbt(d, false); + limit_buf_size_after_use(); + } + } else if (ret == DB_BUFFER_SMALL) { + // Either the key or data DBTs might trigger a + // DB_KEYSMALL return. Only enlarge the DBT if it + // is actually too small. + if (((sz = d.get_size()) > 0) && (sz > d.get_ulen())) + enlarge_dbt(d, sz); + + if (((sz = k.get_size()) > 0) && (sz > k.get_ulen())) + enlarge_dbt(k, sz); + + goto retry; + } else { + if (ret == DB_NOTFOUND) { + ret = INVALID_ITERATOR_POSITION; + this->curr_key_.reset(); + this->curr_data_.reset(); + } else if (bulk_retrieval_ && + (getflags & DB_MULTIPLE_KEY)){ + BDBOP(((DBC*)csr_)->dbp-> + get_pagesize(((DBC*)csr_)-> + dbp, &bulk_bufsz), ret); + if (bulk_bufsz > d.get_ulen()) {// buf size error + normalize_bulk_bufsize(bulk_bufsz); + bulk_retrieval_ = bulk_bufsz; + enlarge_dbt(d, bulk_bufsz); + goto retry; + } else + throw_bdb_exception( + "DbCursor<>::increment", ret); + } else + throw_bdb_exception( + "DbCursor<>::increment", ret); + } + + csr_status_ = ret; + return ret; + } + + // After each use of key_buf_ and data_buf_, limit their buffer size to + // a reasonable size so that they don't waste a big memory space. + inline void limit_buf_size_after_use() + { + if (bulk_retrieval_) + // Bulk buffer has to be huge, so don't check it. + return; + + if (key_buf_.get_ulen() > DBSTL_MAX_KEY_BUF_LEN) { + key_buf_.set_data(DbstlReAlloc(key_buf_.get_data(), + DBSTL_MAX_KEY_BUF_LEN)); + key_buf_.set_ulen(DBSTL_MAX_KEY_BUF_LEN); + } + if (data_buf_.get_ulen() > DBSTL_MAX_DATA_BUF_LEN) { + data_buf_.set_data(DbstlReAlloc(data_buf_.get_data(), + DBSTL_MAX_DATA_BUF_LEN)); + data_buf_.set_ulen(DBSTL_MAX_DATA_BUF_LEN); + } + } + + // Duplicate this object's cursor and set it to dbc1. + // + inline int dup(DbCursor<key_dt, data_dt>& dbc1) const + { + Dbc* pcsr = 0; + int ret; + + if (csr_ != 0 && csr_->dup(&pcsr, DB_POSITION) == 0) { + dbc1.set_cursor(pcsr); + dbc1.set_owner_db(this->get_owner_db()); + dbc1.set_owner_txn(this->get_owner_txn()); + ResourceManager::instance()->add_cursor( + this->get_owner_db(), &dbc1); + ret = 0; + } else + ret = ITERATOR_DUP_ERROR; + + return ret; + } + +public: + // Open a cursor, do not move it, it is at an invalid position. + // All cursors should be opened using this method. + // + inline int open(db_container *pdbc, int flags) + { + int ret; + + Db *pdb = pdbc->get_db_handle(); + if (pdb == NULL) + return 0; + if (csr_) // Close before open. + return 0; + ret = ResourceManager::instance()-> + open_cursor(this, pdb, flags); + set_rmw(rmw_get_); + this->csr_status_ = ret; + return ret; + } + + // Move Berkeley DB cursor to specified key k, by default use DB_SET, + // but DB_SET_RANGE can and may also be used. + // + int move_to(const key_dt&k, u_int32_t flag = DB_SET) + { + Dbt &d1 = data_buf_; + int ret; + u_int32_t sz; + DataItem k1(k, true); + + if (csr_ == NULL) + return INVALID_ITERATOR_CURSOR; + + curr_key_.reset(); + curr_data_.reset(); + inform_duppers(); + + // It is likely that k is not in db, causing get(DB_SET) to + // fail, we should not throw an exception because of this. + // + if (rmw_get_) + flag |= DB_RMW; +retry: ret = csr_->get(&k1.get_dbt(), &d1, flag); + if (ret == 0) { + curr_key_ = k1; + curr_data_.set_dbt(d1, false); + limit_buf_size_after_use(); + } else if (ret == DB_BUFFER_SMALL) { + sz = d1.get_size(); + assert(sz > 0); + enlarge_dbt(d1, sz); + goto retry; + } else { + if (ret == DB_NOTFOUND) { + ret = INVALID_ITERATOR_POSITION; + // Invalidate current values because it is + // at an invalid position. + this->curr_key_.reset(); + this->curr_data_.reset(); + } else + throw_bdb_exception("DbCursor<>::move_to", ret); + } + + csr_status_ = ret; + return ret; + } + + // Returns the number of keys equal to the current one. + inline size_t count() + { + int ret; + db_recno_t cnt; + + BDBOP2(csr_->count(&cnt, 0), ret, close()); + return (size_t)cnt; + } + + int insert(const key_dt&k, const data_dt& d, int pos = DB_BEFORE) + { + // !!!XXX: + // We do a deep copy of the input data into a local + // variable. Apparently not doing so causes issues + // when using gcc. Even though the put completes prior + // to returning from this function call. + // It would be best to avoid this additional copy. + int ret; + // (k, d) pair may be a temporary pair, so we must copy them. + DataItem k1(k, false), d1(d, false); + + inform_duppers(); + if (pos == DB_AFTER) { + ret = this->csr_->put(&k1.get_dbt(), &d1.get_dbt(), + pos); + // May be using this flag for an empty database, + // because begin() an iterator of an empty db_vector + // equals its end() iterator, so use DB_KEYLAST to + // retry. + // + if (ret == EINVAL || ret == 0) + return ret; + else if (ret) + throw_bdb_exception("DbCursor<>::insert", ret); + } + if (pos == DB_NODUPDATA) + BDBOP3(this->csr_->put(&k1.get_dbt(), &d1.get_dbt(), + pos), ret, DB_KEYEXIST, close()); + else + BDBOP2(this->csr_->put(&k1.get_dbt(), &d1.get_dbt(), + pos), ret, close()); + this->csr_status_ = ret; + if (ret == 0) { + curr_key_ = k1; + curr_data_ = d1; + } + // This cursor points to the new key/data pair now. + return ret; + } + + // Replace current cursor-pointed data item with d. + inline int replace(const data_dt& d) + { + Dbt k1; + int ret; + // !!!XXX: + // We do a deep copy of the input data into a local + // variable. Apparently not doing so causes issues + // when using gcc. Even though the put completes prior + // to returning from this function call. + // It would be best to avoid this additional copy. + // d may be a temporary object, so we must copy it. + DataItem d1(d, false); + + + BDBOP2(this->csr_->put(&k1, &d1.get_dbt(), DB_CURRENT), + ret, close()); + curr_data_ = d1; // Update current data. + + this->csr_status_ = ret; + return ret; + } + + // Remove old key and insert new key-psuodo_data. First insert then + // move to old key and remove it so that the cursor remains at the + // old key's position, according to DB documentation. + // But from practice I can see + // the cursor after delete seems not at old position because a for + // loop iteration exits prematurelly, not all elements are passed. + // + inline int replace_key(const key_dt&k) + { + data_dt d; + key_dt k0; + int ret; + + this->get_current_key_data(k0, d); + if (k0 == k) + return 0; + + DbCursor<key_dt, data_dt> csr2; + this->dup(csr2); + // Delete current, then insert new key/data pair. + ret = csr2.del(); + ret = csr2.insert(k, d, DB_KEYLAST); + this->csr_status_ = ret; + + // Now this->csr_ is sitting on an invalid position, its + // iterator is invalidated. Must first move it to the next + // position before using it. + return ret; + } + + inline int del() + { + int ret; + + inform_duppers(); + BDBOP2(csr_->del(0), ret, close()); + + // By default pos.csr_ will stay at where it was after delete, + // which now is an invalid position. So we need to move to + // next to conform to stl specifications, but we don't move it + // here, iterator::erase should move the iterator itself + // forward. + // + this->csr_status_ = ret; + return ret; + } + + // Make sure the bulk buffer is large enough, and a multiple of 1KB. + // This function may be called prior to cursor initialization, it is + // not possible to verify that the buffer size is a multiple of the + // page size here. + u_int32_t normalize_bulk_bufsize(u_int32_t &bulksz) + { + if (bulksz == 0) + return 0; + + while (bulksz < 16 * sizeof(data_dt)) + bulksz *= 2; + + bulksz = bulksz + 1024 - bulksz % 1024; + + return bulksz; + } + + //////////////////////////////////////////////////////////////////// + // + // Begin public constructors and destructor. + // + explicit DbCursor(u_int32_t b_bulk_retrieval = 0, bool brmw1 = false, + bool directdbget = true) : DbCursorBase(), + curr_key_(sizeof(key_dt)), curr_data_(sizeof(data_dt)) + { + u_int32_t bulksz = sizeof(data_dt); // non-bulk + rmw_get_ = brmw1; + this->bulk_retrieval_ = + normalize_bulk_bufsize(b_bulk_retrieval); + recno_itr_ = NULL; + multi_itr_ = NULL; + + if (bulk_retrieval_) { + if (bulksz <= bulk_retrieval_) + bulksz = bulk_retrieval_; + else { + normalize_bulk_bufsize(bulksz); + bulk_retrieval_ = bulksz; + } + } + key_buf_.set_data(DbstlMalloc(sizeof(key_dt))); + key_buf_.set_ulen(sizeof(key_dt)); + key_buf_.set_flags(DB_DBT_USERMEM); + data_buf_.set_data(DbstlMalloc(bulksz)); + data_buf_.set_ulen(bulksz); + data_buf_.set_flags(DB_DBT_USERMEM); + directdb_get_ = directdbget; + } + + // Copy constructor, duplicate cursor here. + DbCursor(const DbCursor<key_dt, data_dt>& dbc) : + DbCursorBase(dbc), + curr_key_(dbc.curr_key_), curr_data_(dbc.curr_data_) + { + void *pk, *pd; + + dbc.dup(*this); + csr_status_ = dbc.csr_status_; + if (csr_ || dbc.csr_) + this->rmw_get_ = set_rmw(dbc.rmw_get_, + ((DBC*)dbc.csr_)->dbenv); + else + rmw_get_ = dbc.rmw_get_; + + bulk_retrieval_ = dbc.bulk_retrieval_; + + // Now we have to copy key_buf_ and data_buf_ to support + // multiple retrieval. + key_buf_.set_data(pk = DbstlMalloc(dbc.key_buf_.get_ulen())); + key_buf_.set_ulen(dbc.key_buf_.get_ulen()); + key_buf_.set_size(dbc.key_buf_.get_size()); + key_buf_.set_flags(DB_DBT_USERMEM); + memcpy(pk, dbc.key_buf_.get_data(), key_buf_.get_ulen()); + + data_buf_.set_data(pd = DbstlMalloc(dbc.data_buf_.get_ulen())); + data_buf_.set_ulen(dbc.data_buf_.get_ulen()); + data_buf_.set_size(dbc.data_buf_.get_size()); + data_buf_.set_flags(DB_DBT_USERMEM); + memcpy(pd, dbc.data_buf_.get_data(), data_buf_.get_ulen()); + if (dbc.recno_itr_) { + recno_itr_ = new DbstlMultipleRecnoDataIterator( + data_buf_); + recno_itr_->set_pointer(dbc.recno_itr_->get_pointer()); + } else + recno_itr_ = NULL; + if (dbc.multi_itr_) { + multi_itr_ = new DbstlMultipleKeyDataIterator( + data_buf_); + multi_itr_->set_pointer(dbc.multi_itr_->get_pointer()); + + } else + multi_itr_ = NULL; + + directdb_get_ = dbc.directdb_get_; + + // Do not copy sduppers, they are private to each DbCursor<> + // object. + } + + virtual ~DbCursor() + { + close(); // Call close() ahead of freeing following buffers. + free(key_buf_.get_data()); + free(data_buf_.get_data()); + if (multi_itr_) + delete multi_itr_; + if (recno_itr_) + delete recno_itr_; + } + + //////////////////////////////////////////////////////////////////// + + const DbCursor<key_dt, data_dt>& operator= + (const DbCursor<key_dt, data_dt>& dbc) + { + void *pk; + u_int32_t ulen; + + DbCursorBase::operator =(dbc); + dbc.dup(*this); + curr_key_ = dbc.curr_key_; + curr_data_ = dbc.curr_data_; + rmw_get_ = dbc.rmw_get_; + this->bulk_retrieval_ = dbc.bulk_retrieval_; + this->directdb_get_ = dbc.directdb_get_; + // Now we have to copy key_buf_ and data_buf_ to support + // bulk retrieval. + key_buf_.set_data(pk = DbstlReAlloc(key_buf_.get_data(), + ulen = dbc.key_buf_.get_ulen())); + key_buf_.set_ulen(ulen); + key_buf_.set_size(dbc.key_buf_.get_size()); + key_buf_.set_flags(DB_DBT_USERMEM); + memcpy(pk, dbc.key_buf_.get_data(), ulen); + + data_buf_.set_data(pk = DbstlReAlloc(key_buf_.get_data(), + ulen = dbc.key_buf_.get_ulen())); + data_buf_.set_ulen(ulen); + data_buf_.set_size(dbc.data_buf_.get_size()); + data_buf_.set_flags(DB_DBT_USERMEM); + memcpy(pk, dbc.key_buf_.get_data(), ulen); + + if (dbc.recno_itr_) { + if (recno_itr_) { + delete recno_itr_; + recno_itr_ = NULL; + } + recno_itr_ = new DbstlMultipleRecnoDataIterator( + data_buf_); + recno_itr_->set_pointer(dbc.recno_itr_->get_pointer()); + } else if (recno_itr_) { + delete recno_itr_; + recno_itr_ = NULL; + } + + if (dbc.multi_itr_) { + if (multi_itr_) { + delete multi_itr_; + multi_itr_ = NULL; + } + multi_itr_ = new DbstlMultipleKeyDataIterator( + data_buf_); + multi_itr_->set_pointer(dbc.multi_itr_->get_pointer()); + + } else if (multi_itr_) { + delete multi_itr_; + multi_itr_ = NULL; + } + + return dbc; + // Do not copy sduppers, they are private to each DbCursor<> + // object. + + } + + // Move Dbc*cursor to next position. If doing bulk read, read from + // the bulk buffer. If bulk buffer exhausted, do another bulk read + // from database, and then read from the bulk buffer. Quit if no + // more data in database. + // + int next(int flag = DB_NEXT) + { + Dbt k, d; + db_recno_t recno; + int ret; + +retry: if (bulk_retrieval_) { + if (multi_itr_) { + if (multi_itr_->next(k, d)) { + curr_key_.set_dbt(k, false); + curr_data_.set_dbt(d, false); + return 0; + } else { + delete multi_itr_; + multi_itr_ = NULL; + } + } + if (recno_itr_) { + if (recno_itr_->next(recno, d)) { + curr_key_.set_dbt(k, false); + curr_data_.set_dbt(d, false); + return 0; + } else { + delete recno_itr_; + recno_itr_ = NULL; + } + } + } + ret = increment(flag); + if (bulk_retrieval_ && ret == 0) + goto retry; + return ret; + } + + inline int prev(int flag = DB_PREV) + { + return increment(flag); + } + + // Move Dbc*cursor to first element. If doing bulk read, read data + // from bulk buffer. + int first() + { + Dbt k, d; + db_recno_t recno; + int ret; + + ret = increment(DB_FIRST); + if (bulk_retrieval_) { + if (multi_itr_) { + if (multi_itr_->next(k, d)) { + curr_key_.set_dbt(k, false); + curr_data_.set_dbt(d, false); + return 0; + } else { + delete multi_itr_; + multi_itr_ = NULL; + } + } + if (recno_itr_) { + if (recno_itr_->next(recno, d)) { + curr_key_.set_dbt(k, false); + curr_data_.set_dbt(d, false); + return 0; + } else { + delete recno_itr_; + recno_itr_ = NULL; + } + } + } + + return ret; + } + + inline int last() + { + return increment(DB_LAST); + } + + // Get current key/data pair, shallow copy. Return 0 on success, + // -1 if no data. + inline int get_current_key_data(key_dt&k, data_dt&d) + { + if (directdb_get_) + update_current_key_data_from_db( + DbCursorBase::SKIP_NONE); + if (curr_key_.get_data(k) == 0 && curr_data_.get_data(d) == 0) + return 0; + else + return INVALID_KEY_DATA; + } + + // Get current data, shallow copy. Return 0 on success, -1 if no data. + inline int get_current_data(data_dt&d) + { + if (directdb_get_) + update_current_key_data_from_db(DbCursorBase::SKIP_KEY); + if (curr_data_.get_data(d) == 0) + return 0; + else + return INVALID_KEY_DATA; + } + + // Get current key, shallow copy. Return 0 on success, -1 if no data. + inline int get_current_key(key_dt&k) + { + if (directdb_get_) + update_current_key_data_from_db( + DbCursorBase::SKIP_DATA); + if (curr_key_.get_data(k) == 0) + return 0; + else + return INVALID_KEY_DATA; + } + + inline void close() + { + if (csr_) { + inform_duppers(); + ResourceManager::instance()->remove_cursor(this); + } + csr_ = NULL; + } + + // Parameter skipkd specifies skip retrieving key or data: + // If 0, don't skip, retrieve both; + // If 1, skip retrieving key; + // If 2, skip retrieving data. + // Do not poll from db again if doing bulk retrieval. + void update_current_key_data_from_db(DbcGetSkipOptions skipkd) { + int ret; + u_int32_t sz, sz1, kflags = DB_DBT_USERMEM, + dflags = DB_DBT_USERMEM; + // Do not poll from db again if doing bulk retrieval. + if (this->bulk_retrieval_) + return; + if (this->csr_status_ != 0) { + curr_key_.reset(); + curr_data_.reset(); + return; + } + + // We will modify flags if skip key or data, so cache old + // value and set it after get calls. + if (skipkd != DbCursorBase::SKIP_NONE) { + kflags = key_buf_.get_flags(); + dflags = data_buf_.get_flags(); + } + if (skipkd == DbCursorBase::SKIP_KEY) { + key_buf_.set_dlen(0); + key_buf_.set_flags(DB_DBT_PARTIAL | DB_DBT_USERMEM); + } + + if (skipkd == DbCursorBase::SKIP_DATA) { + data_buf_.set_dlen(0); + data_buf_.set_flags(DB_DBT_PARTIAL | DB_DBT_USERMEM); + } +retry: ret = csr_->get(&key_buf_, &data_buf_, DB_CURRENT); + if (ret == 0) { + if (skipkd != DbCursorBase::SKIP_KEY) + curr_key_ = key_buf_; + if (skipkd != DbCursorBase::SKIP_DATA) + curr_data_ = data_buf_; + limit_buf_size_after_use(); + } else if (ret == DB_BUFFER_SMALL) { + if ((sz = key_buf_.get_size()) > 0) + enlarge_dbt(key_buf_, sz); + if ((sz1 = data_buf_.get_size()) > 0) + enlarge_dbt(data_buf_, sz1); + if (sz == 0 && sz1 == 0) + THROW0(InvalidDbtException); + goto retry; + } else { + if (skipkd != DbCursorBase::SKIP_NONE) { + key_buf_.set_flags(kflags); + data_buf_.set_flags(dflags); + } + throw_bdb_exception( + "DbCursor<>::update_current_key_data_from_db", ret); + } + + if (skipkd != DbCursorBase::SKIP_NONE) { + key_buf_.set_flags(kflags); + data_buf_.set_flags(dflags); + } + } +}; // DbCursor<> + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// +// RandDbCursor class template definition +// +// RandDbCursor is a random accessible cursor wrapper for use by +// db_vector_iterator, it derives from DbCursor<> class. It has a fixed key +// data type, which is index_type. +// +typedef db_recno_t index_type; +template<Typename data_dt> +class RandDbCursor : public DbCursor<index_type, data_dt> +{ +protected: + friend class DataItem; + typedef ssize_t difference_type; +public: + typedef RandDbCursor<data_dt> self; + typedef DbCursor<index_type, data_dt> base; + + // Return current csr_ pointed element's index in recno database + // (i.e. the index starting from 1). csr_ must be open and + // point to an existing key/data pair. + // + inline index_type get_current_index() const + { + index_type ndx; + + if (this->directdb_get_) + ((self *)this)->update_current_key_data_from_db( + DbCursorBase::SKIP_DATA); + this->curr_key_.get_data(ndx); + return ndx; + } + + inline int compare(const self *csr2) const{ + index_type i1, i2; + + i1 = this->get_current_index(); + i2 = csr2->get_current_index(); + return i1 - i2; + } + + // Insert data d before/after current position. + int insert(const data_dt& d, int pos = DB_BEFORE){ + int k = 1, ret; + //data_dt dta; + + // Inserting into empty db, must set key to 1. + if (pos == DB_KEYLAST) + k = 1; + + ret = base::insert(k, d, pos); + + // Inserting into a empty db using begin() itr, so flag is + // DB_AFTER and surely failed, so change to use DB_KEYLAST + // and try again. + if (ret == EINVAL) { + k = 1; + pos = DB_KEYLAST; + ret = base::insert(k, d, pos); + } + this->csr_status_ = ret; + return ret; + } + + /* + * Move the cursor n positions, if reaches the beginning or end, + * returns DB_NOTFOUND. + */ + int advance(difference_type n) + { + int ret = 0; + index_type indx; + u_int32_t sz, flags = 0; + + indx = this->get_current_index(); + if (n == 0) + return 0; + + index_type i = (index_type)n; + indx += i; + + if (n < 0 && indx < 1) { // Index in recno db starts from 1. + + ret = INVALID_ITERATOR_POSITION; + return ret; + } + this->inform_duppers(); + + // Do a search to determine whether new position is valid. + Dbt k, &d = this->data_buf_; + + + k.set_data(&indx); + k.set_size(sizeof(indx)); + if (this->rmw_get_) + flags |= DB_RMW; + +retry: if (this->csr_ && + ((ret = this->csr_->get(&k, &d, DB_SET)) == DB_NOTFOUND)) { + this->csr_status_ = ret = INVALID_ITERATOR_POSITION; + this->curr_key_.reset(); + this->curr_data_.reset(); + } else if (ret == DB_BUFFER_SMALL) { + sz = d.get_size(); + assert(sz > 0); + this->enlarge_dbt(d, sz); + goto retry; + } else if (ret == 0) { + this->curr_key_.set_dbt(k, false); + this->curr_data_.set_dbt(d, false); + this->limit_buf_size_after_use(); + } else + throw_bdb_exception("RandDbCursor<>::advance", ret); + this->csr_status_ = ret; + return ret; + } + + // Return the last index of recno db (index starting from 1), + // it will also move the underlying cursor to last key/data pair. + // + inline index_type last_index() + { + int ret; + + ret = this->last(); + if (ret) + return 0;// Invalid position. + else + return get_current_index(); + } + + explicit RandDbCursor(u_int32_t b_bulk_retrieval = 0, + bool b_rmw1 = false, bool directdbget = true) + : base(b_bulk_retrieval, b_rmw1, directdbget) + { + } + + RandDbCursor(const RandDbCursor<data_dt>& rdbc) : base(rdbc) + { + } + + explicit RandDbCursor(Dbc* csr1, int posidx = 0) : base(csr1) + { + } + + virtual ~RandDbCursor() + { + } + +}; // RandDbCursor<> + +END_NS //ns dbstl + +#endif // !_DB_STL_DBC_H diff --git a/stl/dbstl_dbt.h b/stl/dbstl_dbt.h new file mode 100644 index 0000000..22d1042 --- /dev/null +++ b/stl/dbstl_dbt.h @@ -0,0 +1,803 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#ifndef _DB_STL_DBT_H +#define _DB_STL_DBT_H + +#include <assert.h> +#include <string> + +#include "dbstl_common.h" +#include "dbstl_exception.h" +#include "dbstl_utility.h" + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +// +// DataItem class template definition +// +// 1. DataItem is a Dbt wrapper, it provides both typed data to/from memory +// chunk mapping as well as iostream support. Note that iostream functionality +// is not yet implemented. +// 2. DataItem is used inside dbstl to provide consistent Dbt object memory +// management. +// 3. DataItem is not only capable of mapping fixed size objects, but also +// varying length objects and objects not located in a consecutive chunk of +// memory, with the condition that user configures the required methods in +// DbstlElemTraits. +// 4. DataItem can not be a class template because inside it, the "member +// function template override" support is needed. +// +START_NS(dbstl) + +using std::string; +#ifdef HAVE_WSTRING +using std::wstring; +#endif + +class DataItem +{ +private: + typedef DataItem self; + + //////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////// + // + // DataItem memory management + // + // The dbt_ member is the current dbt, data is stored in the dbt's + // referenced memory, it may + // deep copy from constructor and from other Dbt, depending on + // the constructors "onstack" parameter --- if true, this object + // is only used as a stack object inside a function, + // so do shallow copy; otherwise do deep copy. + // There is always a DB_DBT_USERMEM flag set to the dbt, + // its ulen data member stores the length of referenced memory, + // its size data member stores the actual size of data; + // If onstack is true, its dlen is INVALID_DLEN, and freemem() + // will not free such memory because this object only reference + // other object's memory, its the referenced object's responsibility + // to free their memory. + // + // A DataItem object is not used everywhere, so it is impossible for + // such an object to have two kinds of usages as above at the same + // time, so we are safe doing so. + Dbt dbt_; + + // Free dbt_'s referenced memory if that memory is allocated in heap + // and owned by dbt_. + inline void freemem() + { + void *buf = dbt_.get_data(); + + if (buf != NULL && (dbt_.get_flags() & DB_DBT_USERMEM) != 0 + && dbt_.get_dlen() != INVALID_DLEN) + free(buf); + memset(&dbt_, 0, sizeof(dbt_)); + } + +public: + + // Deep copy, because dbt2.data pointed memory may be short lived. + inline void set_dbt(const DbstlDbt&dbt2, bool onstack) + { + void *buf; + u_int32_t s1, s2; + DBT *pdbt2, *pdbt; + + pdbt2 = (DBT *)&dbt2; + pdbt = (DBT *)&dbt_; + + if (!onstack) { + buf = pdbt->data; + s1 = pdbt->ulen; + s2 = pdbt2->size; + if(s2 > s1) { + buf = DbstlReAlloc(buf, s2); + pdbt->size = s2; + pdbt->data = buf; + pdbt->ulen = s2; + pdbt->flags |= DB_DBT_USERMEM; + } else + pdbt->size = s2; + memcpy(buf, pdbt2->data, s2); + } else { + freemem(); + dbt_ = (const Dbt)dbt2; + pdbt->dlen = (INVALID_DLEN); + } + } + + // Deep copy, because dbt2.data pointed memory may be short lived. + inline void set_dbt(const Dbt&dbt2, bool onstack) + { + void *buf; + u_int32_t s1, s2; + DBT *pdbt2, *pdbt; + + pdbt2 = (DBT *)&dbt2; + pdbt = (DBT *)&dbt_; + + if (!onstack) { + buf = pdbt->data; + s1 = pdbt->ulen; + s2 = pdbt2->size; + if(s2 > s1) { + buf = DbstlReAlloc(buf, s2); + pdbt->size = s2; + pdbt->data = buf; + pdbt->ulen = s2; + pdbt->flags |= DB_DBT_USERMEM; + } else + pdbt->size = s2; + memcpy(buf, pdbt2->data, s2); + } else { + freemem(); + dbt_ = dbt2; + pdbt->dlen = (INVALID_DLEN); + } + } + + inline void set_dbt(const DBT&dbt2, bool onstack) + { + void *buf; + u_int32_t s1, s2; + DBT *pdbt = (DBT *)&dbt_; + + if (!onstack) { + buf = pdbt->data; + s1 = pdbt->ulen; + s2 = dbt2.size; + if(s2 > s1) { + buf = DbstlReAlloc(buf, s2); + pdbt->size = s2; + pdbt->data = buf; + pdbt->ulen = s2; + pdbt->flags |= DB_DBT_USERMEM; + } else + pdbt->size = s2; + memcpy(buf, dbt2.data, s2); + } else { + freemem(); + // The following is right because Dbt derives from + // DBT with no extra members or any virtual functions. + memcpy(&dbt_, &dbt2, sizeof(dbt2)); + pdbt->dlen = INVALID_DLEN; + } + } + + // Return to the initial state. + inline void reset() + { + void *buf = dbt_.get_data(); + if (buf) { + memset(buf, 0, dbt_.get_ulen()); + dbt_.set_size(0); + } + } + + inline Dbt& get_dbt() + { + return dbt_; + } + + // Return data of this object. If no data return -1, if it has data + // return 0. + // + // !!!XXX Note that the type parameter T can only be in this function + // because "template type parameter overload" applies only to a + // functions template argument list, rather than that of classes. + // If you put the "template<Typename T>" to this class's declaration, + // making it a class template, then when T is any of Dbt, DBT, or + // DataItem<T>, there will be two copies of this function. One will be + // this function's instantiated version, the other one is one of the + // three functions defined below. + // + template <Typename T> + inline int get_data(T& data) const + { + int ret; + typedef DbstlElemTraits<T> EM; + typename EM::ElemRstoreFunct restore; + void *pdata = NULL; + + if ((pdata = dbt_.get_data()) != NULL) { + if ((restore = EM::instance()-> + get_restore_function()) != NULL) + restore(data, pdata); + else + data = *((T*)pdata); + ret = 0; + } else + ret = -1; + return ret; + } + + //////////////////////////////////////////////////////////////////// + // + // Begin functions supporting direct naked string storage. + // + // Always store the data, rather than the container object. + // + // The returned string lives no longer than the next iterator + // movement call. + // + inline int get_data(char*& data) const + { + data = (char*)dbt_.get_data(); + return 0; + } + + inline int get_data(string &data) const + { + data = (string::pointer) dbt_.get_data(); + return 0; + } + + inline int get_data(wchar_t*& data) const + { + data = (wchar_t*)dbt_.get_data(); + return 0; + } + +#ifdef HAVE_WSTRING + inline int get_data(wstring &data) const + { + data = (wstring::pointer) dbt_.get_data(); + return 0; + } +#endif + + //////////////////////////////////////////////////////////////////// + + // Supporting storing arbitrary type of sequence. + template <Typename T> + inline int get_data(T*& data) const + { + data = (T*)dbt_.get_data(); + return 0; + } + + inline int get_data(DataItem& data) const + { + int ret; + + if (dbt_.get_data()) { + data.set_dbt(dbt_, false); + ret = 0; + } else + ret = -1; + return ret; + } + + //////////////////////////////////////////////////////////////////// + // + // Begin functions supporting Dbt storage. + // + // This member function allows storing a Dbt type, so that user can + // store the varying length data into Dbt. + // + // This method is required to copy a data element's bytes to another + // Dbt object, used inside by dbstl. + // If there is no data return -1, if it has data return 0. + // + inline int get_data(Dbt& data) const + { + int ret; + void *addr; + u_int32_t sz; + DBT *pdbt = (DBT *)&dbt_, *pdata = (DBT *)&data; + + if (pdbt->data) { + addr = pdata->data; + sz = pdbt->size; + if (pdata->ulen < sz) { + pdata->data = DbstlReAlloc(addr, sz); + pdata->size = sz; + pdata->ulen = sz; + pdata->flags |= DB_DBT_USERMEM; + } else + pdata->size = sz; + memcpy(pdata->data, pdbt->data, sz); + ret = 0; + } else + ret = -1; + return ret; + } + + inline int get_data(DBT& data) const + { + int ret; + void*addr; + u_int32_t sz; + + if (dbt_.get_data()) { + addr = data.data; + if (data.ulen < (sz = dbt_.get_size())) { + data.data = DbstlReAlloc(addr, sz); + // User need to free this memory + data.flags = data.flags | DB_DBT_USERMEM; + data.size = sz; + data.ulen = sz; + } else + data.size = sz; + memcpy(data.data, dbt_.get_data(), sz); + ret = 0; + } else + ret = -1; + return ret; + } + + inline int get_data(DbstlDbt& data) const + { + int ret; + void *addr; + u_int32_t sz; + DBT *pdbt = (DBT *)&dbt_, *pdata = (DBT *)&data; + + if (pdbt->data) { + addr = pdata->data; + sz = pdbt->size; + if (pdata->ulen < sz) { + pdata->data = DbstlReAlloc(addr, sz); + pdata->size = sz; + pdata->ulen = sz; + pdata->flags |= DB_DBT_USERMEM; + } else + pdata->size = sz; + memcpy(pdata->data, pdbt->data, sz); + ret = 0; + } else + ret = -1; + return ret; + } + + //////////////////////////////////////////////////////////////////// + + // Deep copy in assignment and copy constructor. + inline const DbstlDbt& operator=(const DbstlDbt& t2) + { + set_dbt(t2, false); + return t2; + } + + // Deep copy in assignment and copy constructor. + inline const Dbt& operator=(const Dbt& t2) + { + set_dbt(t2, false); + return t2; + } + + // Deep copy in assignment and copy constructor. + inline const DBT& operator=(const DBT& t2) + { + set_dbt(t2, false); + return t2; + } + + // Deep copy in assignment and copy constructor. + template <Typename T> + inline const T& operator = (const T&dt) + { + + make_dbt(dt, false); + return dt; + } + + // Generic way of storing an object or variable. Note that DataItem + // is not a class template but a class with function templates. + // Variable t locates on a consecutive chunk of memory, and objects + // of T have the same size. + // + template <Typename T> + void make_dbt(const T& dt, bool onstack) + { + typedef DbstlElemTraits<T> EM; + u_int32_t sz; + typename EM::ElemSizeFunct sizef; + typename EM::ElemCopyFunct copyf; + DBT *pdbt = (DBT *)&dbt_; + + if ((sizef = EM::instance()->get_size_function()) != NULL) + sz = sizef(dt); + else + sz = sizeof(dt); + + if (onstack) { + freemem(); + pdbt->data = ((void*)&dt); + // We have to set DB_DBT_USERMEM for DB_THREAD to work. + pdbt->flags = (DB_DBT_USERMEM); + pdbt->size = (sz); + pdbt->ulen = (sz); + // This is a flag that this memory can't be freed + // because it is on stack. + pdbt->dlen = (INVALID_DLEN); + return; + } + + // Not on stack, allocate enough space and "copy" the object + // using shall copy or customized copy. + if (pdbt->ulen < sz) { + pdbt->data = (DbstlReAlloc(pdbt->data, sz)); + assert(pdbt->data != NULL); + pdbt->size = (sz); + pdbt->ulen = (sz); + pdbt->flags = (DB_DBT_USERMEM); + } else + pdbt->size = (sz); + + if ((copyf = EM::instance()->get_copy_function()) != NULL) + copyf(pdbt->data, dt); + else + memcpy(pdbt->data, &dt, sz); + } + + inline const char*&operator = (const char*&dt) + { + make_dbt(dt, false); + return dt; + } + + inline const wchar_t*&operator = (const wchar_t*&dt) + { + make_dbt(dt, false); + return dt; + } + + inline const string &operator=(const string &dt) + { + make_dbt(dt, false); + return dt; + } + +#ifdef HAVE_WSTRING + inline const wstring &operator=(const wstring &dt) + { + make_dbt(dt, false); + return dt; + } +#endif + + template <Typename T> + inline const T*&operator = (const T*&dt) + { + make_dbt(dt, false); + return dt; + } + + inline const self& operator=(const self&dbt1) + { + ASSIGNMENT_PREDCOND(dbt1) + this->set_dbt(dbt1.dbt_, false); + return dbt1; + } + + // Deep copy. + inline DataItem(const self&dbt1) + { + set_dbt(dbt1.dbt_, false); + } + + + inline DataItem(u_int32_t sz) + { + void *buf; + DBT *pdbt = (DBT *)&dbt_; + + buf = NULL; + buf = DbstlMalloc(sz); + memset(buf, 0, sz); + pdbt->size = sz; + pdbt->ulen = sz; + pdbt->data = buf; + pdbt->flags = DB_DBT_USERMEM; + } + + // Deep copy. The onstack parameter means whether the object referenced + // by this DataItem is on used with a function call where this DataItem + // object is used. If so, we don't deep copy the object, simply refer + // to its memory location. The meaining is the same for this parameter + // in constructors that follow. + inline DataItem(const Dbt&dbt2, bool onstack) + { + set_dbt(dbt2, onstack); + } + + inline DataItem(const DbstlDbt&dbt2, bool onstack) + { + set_dbt(dbt2, onstack); + } + + inline DataItem(const DBT&dbt2, bool onstack) + { + set_dbt(dbt2, onstack); + } + + // Deep copy. There is a partial specialization for char*/wchar_t*/ + // string/wstring. + template<Typename T> + inline DataItem(const T& dt, bool onstack) + { + make_dbt(dt, onstack); + } + + inline ~DataItem(void) + { + freemem(); + } + +protected: + + // Store a char*/wchar_t* string. Need four versions for char* + // and wchar_t* respectively to catch all + // possibilities otherwise the most generic one will be called. + // Note that the two const decorator matters when doing type + // matching. + inline void make_dbt_chars(const char *t, bool onstack) + { + DBT *d = (DBT *)&dbt_; + u_int32_t sz; + sz = ((t == NULL) ? + sizeof(char) : + (u_int32_t)((strlen(t) + 1) * sizeof(char))); + if (!onstack) { + if (d->ulen < sz) { + d->flags |= DB_DBT_USERMEM; + d->data = DbstlReAlloc(d->data, sz); + d->ulen = sz; + } + d->size = sz; + if (t != NULL) + strcpy((char*)d->data, t); + else + memset(d->data, '\0', sizeof(char)); + } else { + freemem(); + d->data = ((t == NULL) ? (void *)"" : (void *)t); + d->size = sz; + d->ulen = sz; + d->flags = (DB_DBT_USERMEM); + d->dlen = (INVALID_DLEN); + } + } + + inline void make_dbt_wchars(const wchar_t *t, bool onstack) + { + DBT *d = (DBT *)&dbt_; + u_int32_t sz; + sz = ((t == NULL) ? + sizeof(wchar_t) : + (u_int32_t)((wcslen(t) + 1) * sizeof(wchar_t))); + if (!onstack) { + if (d->ulen < sz) { + d->flags |= DB_DBT_USERMEM; + d->data = DbstlReAlloc(d->data, sz); + d->ulen = sz; + } + d->size = sz; + if (t != NULL) + wcscpy((wchar_t*)d->data, t); + else + memset(d->data, L'\0', sizeof(wchar_t)); + } else { + freemem(); + d->data = ((t == NULL) ? (void *)L"" : (void *)t); + d->size = sz; + d->ulen = sz; + d->flags = (DB_DBT_USERMEM); + d->dlen = (INVALID_DLEN); + } + } + + inline void make_dbt(const char*& t, bool onstack) + { + make_dbt_chars(t, onstack); + } + + inline void make_dbt(const char* const& t, bool onstack) + { + make_dbt_chars(t, onstack); + } + + inline void make_dbt(char*& t, bool onstack) + { + make_dbt_chars(t, onstack); + } + + inline void make_dbt(char* const& t, bool onstack) + { + make_dbt_chars(t, onstack); + } + + inline void make_dbt(const string& t, bool onstack) + { + make_dbt_chars(t.c_str(), onstack); + } + + inline void make_dbt(const wchar_t*& t, bool onstack) + { + make_dbt_wchars(t, onstack); + } + + inline void make_dbt(const wchar_t* const& t, bool onstack) + { + make_dbt_wchars(t, onstack); + } + + inline void make_dbt(wchar_t*& t, bool onstack) + { + make_dbt_wchars(t, onstack); + } + + inline void make_dbt(wchar_t* const& t, bool onstack) + { + make_dbt_wchars(t, onstack); + } + +#ifdef HAVE_WSTRING + inline void make_dbt(const wstring& t, bool onstack) + { + make_dbt_wchars(t.c_str(), onstack); + } +#endif + + template <Typename T> + void make_dbt_internal(const T*t, bool onstack) + { + u_int32_t i, sz, totalsz, sql; + DBT *pdbt = (DBT *)&dbt_; + typename DbstlElemTraits<T>::ElemSizeFunct szf = NULL; + typename DbstlElemTraits<T>::SequenceLenFunct + seqlenf = NULL; + + szf = DbstlElemTraits<T>::instance()-> + get_size_function(); + seqlenf = DbstlElemTraits<T>::instance()-> + get_sequence_len_function(); + + assert(seqlenf != NULL); + sql = sz = (u_int32_t)seqlenf(t); + if (szf) + for (i = 0, totalsz = 0; i < sz; i++) + totalsz += szf(t[i]); + else + totalsz = sz * sizeof(T); + + sz = totalsz; + + if (onstack) { + freemem(); + pdbt->data = (void *)t; + pdbt->size = sz; + pdbt->ulen = sz; + pdbt->flags = DB_DBT_USERMEM; + pdbt->dlen = INVALID_DLEN; // onstack flag; + } else { + // ulen stores the real length of the pointed memory. + if (pdbt->ulen < sz) { + pdbt->data = DbstlReAlloc(pdbt->data, sz); + pdbt->ulen = sz; + pdbt->flags |= DB_DBT_USERMEM; + } + pdbt->size = sz; + + DbstlElemTraits<T>::instance()-> + get_sequence_copy_function() + ((T *)pdbt->data, t, sql); + } + } + + // Store a sequence of base type T. Need four versions to catch all + // possibilities otherwise the most generic one will be called. + template <Typename T> + inline void make_dbt(const T*const&tt, bool onstack) + { + make_dbt_internal((const T*)tt, onstack); + } + template <Typename T> + inline void make_dbt(T*const&tt, bool onstack) + { + make_dbt_internal((const T*)tt, onstack); + } + template <Typename T> + inline void make_dbt(T*&tt, bool onstack) + { + make_dbt_internal((const T*)tt, onstack); + } + template <Typename T> + inline void make_dbt(const T*&tt, bool onstack) + { + make_dbt_internal((const T*)tt, onstack); + } + + +public: + inline DataItem(const char*& t, bool onstack) + { + make_dbt_chars(t, onstack); + } + + inline DataItem(const char* const& t, bool onstack) + { + make_dbt_chars(t, onstack); + } + + inline DataItem(char*& t, bool onstack) + { + make_dbt_chars(t, onstack); + } + + inline DataItem(char* const& t, bool onstack) + { + make_dbt_chars(t, onstack); + } + + inline DataItem(const string& t, bool onstack) + { + make_dbt_chars(t.c_str(), onstack); + } + + inline DataItem(const wchar_t*& t, bool onstack) + { + make_dbt_wchars(t, onstack); + } + + inline DataItem(const wchar_t* const& t, bool onstack) + { + make_dbt_wchars(t, onstack); + } + + inline DataItem(wchar_t*& t, bool onstack) + { + make_dbt_wchars(t, onstack); + } + + inline DataItem(wchar_t* const& t, bool onstack) + { + make_dbt_wchars(t, onstack); + } + +#ifdef HAVE_WSTRING + inline DataItem(const wstring& t, bool onstack) + { + make_dbt_wchars(t.c_str(), onstack); + } +#endif + template<Typename T> + inline DataItem(T*&tt, bool onstack) + { + make_dbt_internal((const T*)tt, onstack); + } + + template<Typename T> + inline DataItem(const T*&tt, bool onstack) + { + make_dbt_internal((const T*)tt, onstack); + } + + template<Typename T> + inline DataItem(T*const&tt, bool onstack) + { + make_dbt_internal((const T*)tt, onstack); + } + + template<Typename T> + inline DataItem(const T*const&tt, bool onstack) + { + make_dbt_internal((const T*)tt, onstack); + } + + +}; // DataItem<> + +bool operator==(const Dbt&d1, const Dbt&d2); +bool operator==(const DBT&d1, const DBT&d2); +END_NS + +#endif // !_DB_STL_DBT_H diff --git a/stl/dbstl_element_ref.h b/stl/dbstl_element_ref.h new file mode 100644 index 0000000..492fdc7 --- /dev/null +++ b/stl/dbstl_element_ref.h @@ -0,0 +1,873 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#ifndef _DB_STL_KDPAIR_H +#define _DB_STL_KDPAIR_H + +#include <iostream> + +#include "dbstl_common.h" +#include "dbstl_dbt.h" +#include "dbstl_exception.h" +#include "dbstl_base_iterator.h" +#include "dbstl_utility.h" + +START_NS(dbstl) + +using std::istream; +using std::ostream; +using std::basic_ostream; +using std::basic_istream; + +template <Typename ddt> +class db_base_iterator; +template <Typename ddt> +class ElementHolder; + +/** \ingroup dbstl_helper_classes +\defgroup Element_wrappers ElementRef and ElementHolder wrappers. +An ElementRef and ElementHolder object represents the reference to the +data element referenced by an iterator. Each iterator +object has an ElementRef or ElementHolder object that +stores the data element that the iterator points to. + +The ElementHolder class is used to store primitive types into STL containers. + +The ElementRef class is used to store other types into STL containers. + +The ElementRef and ElementHolder classes have identical interfaces, and are +treated the same by other STL classes. Since the ElementRef class inherits +from the template data class, all methods have a _DB_STL_ prefix to avoid name +clashes. + +An ElementRef or ElementHolder class corresponds to a single iterator instance. +An Element object is generally owned by an iterator object. The ownership +relationship is swapped in some specific situations, specifically for the +dereference and array index operator. +@{ +*/ +/// ElementRef element wrapper for classes and structures. +/// \sa ElementHolder +template <Typename ddt> +class _exported ElementRef : public ddt +{ +public: + typedef ElementRef<ddt> self; + typedef ddt base; + typedef db_base_iterator<ddt> iterator_type; + typedef ddt content_type; // Used by assoc classes. + +private: + // The iterator pointing the data element stored in this object. + iterator_type *_DB_STL_itr_; + + // Whether or not to delete itr on destruction, by default it is + // false because this object is supposed to live in the lifetime of + // its _DB_STL_itr_ owner. But there is one exception: in + // db_vector<>::operator[]/front/back and db_map<>::operator[] + // functions, an ElementRef<T> object has to be + // returned instead of its reference, thus the + // returned ElementRef<> has to live longer than its _DB_STL_itr_, + // thus we new an iterator, and call _DB_STL_SetDelItr() method, + // setting this member to true, + // to tell this object that it should delete the + // _DB_STL_itr_ iterator on destruction, and duplicate the _DB_STL_itr_ + // iterator on copy construction. Although + // std::vector<> returns reference rather than value, this is not a + // problem because the returned ElementRef<> will duplicate cursor and + // still points to the same key/data pair. + // + mutable bool _DB_STL_delete_itr_; + +public: + //////////////////////////////////////////////////////////////////// + // + // Begin constructors and destructor. + // + /// \name Constructors and destructor. + //@{ + /// Destructor. + ~ElementRef() { + if (_DB_STL_delete_itr_) { + // Prevent recursive destruction. + _DB_STL_delete_itr_ = false; + _DB_STL_itr_->delete_me(); + } + } + + /// Constructor. + /// If the pitr parameter is NULL or the default value is used, the + /// object created is a simple wrapper and not connected to a container. + /// If a valid iterator parameter is passed in, the wrapped element will + /// be associated with the matching key/data pair in the underlying + /// container. + /// \param pitr The iterator owning this object. + explicit ElementRef(iterator_type *pitr = NULL) + { + _DB_STL_delete_itr_ = false; + _DB_STL_itr_ = pitr; + } + + /// Constructor. + /// Initializes an ElementRef wrapper without an iterator. It can only + /// be used to wrap a data element in memory, it can't access an + /// unerlying database. + /// \param dt The base class object to initialize this object. + ElementRef(const ddt &dt) : ddt(dt) + { + _DB_STL_delete_itr_ = false; + _DB_STL_itr_ = NULL; + } + + /// Copy constructor. + /// The constructor takes a "deep" copy. The created object will be + /// identical to, but independent from the original object. + /// \param other The object to clone from. + ElementRef(const self &other) : ddt(other) + { + // Duplicate iterator if this object lives longer than + // _DB_STL_itr_. + _DB_STL_delete_itr_ = other._DB_STL_delete_itr_; + if (_DB_STL_delete_itr_) { + // Avoid recursive duplicate iterator calls. + other._DB_STL_delete_itr_ = false; + _DB_STL_itr_ = other._DB_STL_itr_->dup_itr(); + other._DB_STL_delete_itr_ = true; + } else + _DB_STL_itr_ = other._DB_STL_itr_; + } + //@} + //////////////////////////////////////////////////////////////////// + + /// \name Assignment operators. + /// The assignment operators are used to store right-values into the + /// wrapped object, and also to store values into an underlying + /// container. + //@{ + /// Assignment Operator. + /// \param dt2 The data value to assign with. + /// \return The object dt2's reference. + inline const ddt& operator=(const ddt& dt2) + { + *((ddt*)this) = dt2; + if (_DB_STL_itr_ != NULL) { + if (!_DB_STL_itr_->is_set_iterator()) + _DB_STL_itr_->replace_current(dt2); + else + _DB_STL_itr_->replace_current_key(dt2); + } + return dt2; + } + + /// Assignment Operator. + /// \param me The object to assign with. + /// \return The object me's reference. + inline const self& operator=(const self& me) + { + ASSIGNMENT_PREDCOND(me) + *((ddt*)this) = (ddt)me; + if (_DB_STL_itr_ != NULL) { + // This object is the reference of an valid data + // element, so we must keep it that way, we don't + // use me's iterator here. + if (!_DB_STL_itr_->is_set_iterator()) + _DB_STL_itr_->replace_current( + me._DB_STL_value()); + else + _DB_STL_itr_->replace_current_key( + me._DB_STL_value()); + } else if (me._DB_STL_delete_itr_) { + // Duplicate an iterator from me. + _DB_STL_delete_itr_ = true; + me._DB_STL_delete_itr_ = false; + _DB_STL_itr_ = me._DB_STL_itr_->dup_itr(); + me._DB_STL_delete_itr_ = true; + } + + return me; + } + //@} + + /// Function to store the data element. + /// The user needs to call this method after modifying the underlying + /// object, so that the version stored in the container can be updated. + /// + /// When db_base_iterator's directdb_get_ member is true, this function + /// must be called after modifying the data member and before any + /// subsequent container iterator dereference operations. If this step + /// is not carried out any changes will be lost. + /// + /// If the data element is changed via ElementHolder<>::operator=(), + /// you don't need to call this function. + inline void _DB_STL_StoreElement() + { + assert(_DB_STL_itr_ != NULL); + _DB_STL_itr_->replace_current(*this); + } + + /// Returns the data element this wrapper object wraps. + inline const ddt& _DB_STL_value() const + { + return *((ddt*)this); + } + + /// Returns the data element this wrapper object wraps. + inline ddt& _DB_STL_value() + { + return *((ddt*)this); + } + +#ifndef DOXYGEN_CANNOT_SEE_THIS + //////////////////////////////////////////////////////////////////// + // + // The following methods are not part of the official public API, + // but can't be declared as protected, since it is not possible + // to declare template-specialised classes as friends. + // + + // Call this function to tell this object that it should delete the + // _DB_STL_itr_ iterator because that iterator was allocated in + // the heap. Methods like db_vector/db_map<>::operator[] should call + // this function. + inline void _DB_STL_SetDelItr() + { + _DB_STL_delete_itr_ = true; + } + + // Only copy data into this object, do not store into database. + inline void _DB_STL_CopyData(const self&dt2) + { + *((ddt*)this) = (ddt)dt2; + } + + inline void _DB_STL_CopyData(const ddt&dt2) + { + *((ddt*)this) = dt2; + } + + // Following functions are prefixed with _DB_STL_ to avoid + // potential name clash with ddt members. + // + inline iterator_type* _DB_STL_GetIterator() const + { + return _DB_STL_itr_; + } + + inline int _DB_STL_GetData(ddt& d) const + { + d = *((ddt*)this); + return 0; + } + + inline void _DB_STL_SetIterator(iterator_type*pitr) + { + _DB_STL_itr_ = pitr; + } + + inline void _DB_STL_SetData(const ddt&d) + { + *(ddt*)this = d; + } + //////////////////////////////////////////////////////////////////// + +}; // ElementRef<> +template<typename T> +class DbstlSeqWriter; +#else +}; +#endif // DOXYGEN_CANNOT_SEE_THIS + + +// The ElementHolder class must have an identical public interface to +// the ElementRef class. +/// A wrapper class for primitive types. It has identical usage and public +/// interface to the ElementRef class. +/// \sa ElementRef. +template <typename ptype> +class _exported ElementHolder +{ +protected: + typedef ElementHolder<ptype> self; + + + inline void _DB_STL_put_new_value_to_db() + { + if (_DB_STL_itr_ != NULL) { + if (!_DB_STL_itr_->is_set_iterator()) + _DB_STL_itr_->replace_current(dbstl_my_value_); + else + _DB_STL_itr_->replace_current_key( + dbstl_my_value_); + } + } + + inline void _DB_STL_put_new_value_to_db(const self &me) + { + if (_DB_STL_itr_ != NULL) { + if (!_DB_STL_itr_->is_set_iterator()) + _DB_STL_itr_->replace_current(dbstl_my_value_); + else + _DB_STL_itr_->replace_current_key( + dbstl_my_value_); + } else if (me._DB_STL_delete_itr_) { + // Duplicate an iterator from me. + _DB_STL_delete_itr_ = true; + me._DB_STL_delete_itr_ = false; + _DB_STL_itr_ = me._DB_STL_itr_->dup_itr(); + me._DB_STL_delete_itr_ = true; + } + } + + + +public: + typedef ptype type1; + typedef db_base_iterator<ptype> iterator_type; + typedef ptype content_type; + + //////////////////////////////////////////////////////////////////// + // + // Begin constructors and destructor. + // + /// \name Constructors and destructor. + //@{ + /// Constructor. + /// If the pitr parameter is NULL or the default value is used, the + /// object created is a simple wrapper and not connected to a container. + /// If a valid iterator parameter is passed in, the wrapped element will + /// be associated with the matching key/data pair in the underlying + /// container. + /// \param pitr The iterator owning this object. + explicit inline ElementHolder(iterator_type* pitr = NULL) + { + _DB_STL_delete_itr_ = false; + _DB_STL_itr_ = pitr; + dbstl_str_buf_ = NULL; + dbstl_str_buf_len_ = 0; + memset(&dbstl_my_value_, 0, sizeof(dbstl_my_value_)); + } + + /// Constructor. + /// Initializes an ElementRef wrapper without an iterator. It can only + /// be used to wrap a data element in memory, it can't access an + /// unerlying database. + /// \param dt The base class object to initialize this object. + inline ElementHolder(const ptype&dt) + { + dbstl_str_buf_ = NULL; + dbstl_str_buf_len_ = 0; + _DB_STL_delete_itr_ = false; + _DB_STL_itr_ = NULL; + _DB_STL_CopyData_int(dt); + } + + /// Copy constructor. + /// The constructor takes a "deep" copy. The created object will be + /// identical to, but independent from the original object. + /// \param other The object to clone from. + inline ElementHolder(const self& other) + { + dbstl_str_buf_ = NULL; + dbstl_str_buf_len_ = 0; + _DB_STL_delete_itr_ = other._DB_STL_delete_itr_; + _DB_STL_CopyData(other); + + // Duplicate iterator if this object lives longer than + // _DB_STL_itr_. + _DB_STL_delete_itr_ = other._DB_STL_delete_itr_; + if (_DB_STL_delete_itr_) { + // Avoid recursive duplicate iterator calls. + other._DB_STL_delete_itr_ = false; + _DB_STL_itr_ = other._DB_STL_itr_->dup_itr(); + other._DB_STL_delete_itr_ = true; + } else + _DB_STL_itr_ = other._DB_STL_itr_; + } + + /// Destructor. + ~ElementHolder() { + if (_DB_STL_delete_itr_) { + _DB_STL_delete_itr_ = false; + _DB_STL_itr_->delete_me(); + } + if (dbstl_str_buf_) { + free(dbstl_str_buf_); + dbstl_str_buf_ = NULL; + } + } + //@} + //////////////////////////////////////////////////////////////////// + + /// This operator is a type converter. Where an automatic type + /// conversion is needed, this function is called to convert this + /// object into the primitive type it wraps. + operator ptype () const + { + return dbstl_my_value_; + } + + // ElementHolder is a wrapper for primitive types, and backed by db, + // so we need to override all assignment operations to store updated + // value to database. We don't need to implement other operators for + // primitive types because we have a convert operator which can + // automatically convert to primitive type and use its C++ built in + // operator. + // + /** \name Math operators. + ElementHolder class templates also have all C/C++ self mutating + operators for numeric primitive types, including: + +=, -=, *=, /=, %=, <<=, >>=, &=, |=, ^=, ++, -- + These operators should not be used when ddt is a sequence pointer type + like char* or wchar_t* or T*, otherwise the behavior is undefined. + These methods exist only to override default bahavior to store the + new updated value, otherwise, the type convert operator could have + done all the job. + As you know, some of them are not applicable to float or double types + or ElementHolder wrapper types for float/double types. + These operators not only modifies the cached data element, but also + stores new value to database if it associates a database key/data pair. + @{ + */ + template <Typename T2> + const self& operator +=(const ElementHolder<T2> &p2) + { + dbstl_my_value_ += p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(p2); + return *this; + } + + template <Typename T2> + const self& operator -=(const ElementHolder<T2> &p2) + { + dbstl_my_value_ -= p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(p2); + return *this; + } + template <Typename T2> + const self& operator *=(const ElementHolder<T2> &p2) + { + dbstl_my_value_ *= p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(p2); + return *this; + } + template <Typename T2> + const self& operator /=(const ElementHolder<T2> &p2) + { + dbstl_my_value_ /= p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(p2); + return *this; + } + template <Typename T2> + const self& operator %=(const ElementHolder<T2> &p2) + { + dbstl_my_value_ %= p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(p2); + return *this; + } + + template <Typename T2> + const self& operator &=(const ElementHolder<T2> &p2) + { + dbstl_my_value_ &= p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(p2); + return *this; + } + template <Typename T2> + const self& operator |=(const ElementHolder<T2> &p2) + { + dbstl_my_value_ |= p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(p2); + return *this; + } + template <Typename T2> + const self& operator ^=(const ElementHolder<T2> &p2) + { + dbstl_my_value_ ^= p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(p2); + return *this; + } + + const self& operator >>=(size_t n) + { + dbstl_my_value_ >>= n; + _DB_STL_put_new_value_to_db(); + return *this; + } + + const self& operator <<=(size_t n) + { + dbstl_my_value_ <<= n; + _DB_STL_put_new_value_to_db(); + return *this; + } + + const self& operator ^=(const self &p2) + { + dbstl_my_value_ ^= p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(); + return *this; + } + + const self& operator &=(const self &p2) + { + dbstl_my_value_ &= p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(); + return *this; + } + + const self& operator |=(const self &p2) + { + dbstl_my_value_ |= p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(); + return *this; + } + + const self& operator %=(const self &p2) + { + dbstl_my_value_ %= p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(); + return *this; + } + + const self& operator +=(const self &p2) + { + dbstl_my_value_ += p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(); + return *this; + } + const self& operator -=(const self &p2) + { + dbstl_my_value_ -= p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(); + return *this; + } + const self& operator /=(const self &p2) + { + dbstl_my_value_ /= p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(); + return *this; + } + const self& operator *=(const self &p2) + { + dbstl_my_value_ *= p2.dbstl_my_value_; + _DB_STL_put_new_value_to_db(); + return *this; + } + + self& operator++() + { + dbstl_my_value_++; + _DB_STL_put_new_value_to_db(); + return *this; + } + + self operator++(int) + { + self obj(*this); + dbstl_my_value_++; + _DB_STL_put_new_value_to_db(); + return obj; + } + + self& operator--() + { + dbstl_my_value_--; + _DB_STL_put_new_value_to_db(); + return *this; + } + + self operator--(int) + { + self obj(*this); + dbstl_my_value_--; + _DB_STL_put_new_value_to_db(); + return obj; + } + + inline const ptype& operator=(const ptype& dt2) + { + _DB_STL_CopyData_int(dt2); + _DB_STL_put_new_value_to_db(); + return dt2; + } + + inline const self& operator=(const self& dt2) + { + ASSIGNMENT_PREDCOND(dt2) + _DB_STL_CopyData(dt2); + _DB_STL_put_new_value_to_db(dt2); + return dt2; + } + //@} + + /// Returns the data element this wrapper object wraps; + inline const ptype& _DB_STL_value() const + { + return dbstl_my_value_; + } + + /// Returns the data element this wrapper object wraps; + inline ptype&_DB_STL_value() + { + return dbstl_my_value_; + } + + /// Function to store the data element. + /// The user needs to call this method after modifying the underlying + /// object, so that the version stored in the container can be updated. + /// + /// When db_base_iterator's directdb_get_ member is true, this function + /// must be called after modifying the data member and before any + /// subsequent container iterator dereference operations. If this step + /// is not carried out any changes will be lost. + /// + /// If the data element is changed via ElementHolder<>::operator=(), + /// you don't need to call this function. + inline void _DB_STL_StoreElement() + { + assert(_DB_STL_itr_ != NULL); + _DB_STL_itr_->replace_current(dbstl_my_value_); + } + +#ifndef DOXYGEN_CANNOT_SEE_THIS + //////////////////////////////////////////////////////////////////// + // + // The following methods are not part of the official public API, + // but can't be declared as protected, since it is not possible + // to declare template-specialised classes as friends. + // + inline void _DB_STL_CopyData(const self&dt2) + { + _DB_STL_CopyData_int(dt2.dbstl_my_value_); + } + + template<Typename T> + inline void _DB_STL_CopyData_int(const T&src) + { + dbstl_my_value_ = src; + } + + // Try to catch all types of pointers. + template<Typename T> + inline void _DB_STL_CopyData_int(T* const &src) + { + DbstlSeqWriter<T>::copy_to_holder((ElementHolder<T *> *)this, + (T *)src); + } + + template<Typename T> + inline void _DB_STL_CopyData_int(const T* const &src) + { + DbstlSeqWriter<T>::copy_to_holder((ElementHolder<T *> *)this, + (T *)src); + } + + template<Typename T> + inline void _DB_STL_CopyData_int(T* &src) + { + DbstlSeqWriter<T>::copy_to_holder((ElementHolder<T *> *)this, + (T *)src); + } + + template<Typename T> + inline void _DB_STL_CopyData_int(const T*&src) + { + DbstlSeqWriter<T>::copy_to_holder((ElementHolder<T *> *)this, + (T *)src); + } + + inline iterator_type* _DB_STL_GetIterator() const + { + return _DB_STL_itr_; + } + + inline int _DB_STL_GetData(ptype& d) const + { + d = dbstl_my_value_; + return 0; + } + + inline void _DB_STL_SetIterator(iterator_type*pitr) + { + _DB_STL_itr_ = pitr; + } + + inline void _DB_STL_SetData(const ptype&d) + { + _DB_STL_CopyData_int(d); + } + + inline void _DB_STL_SetDelItr() + { + _DB_STL_delete_itr_ = true; + } + + // The two member has to be public for DbstlSeqWriter to access, + // but can't be accessed by user. + size_t dbstl_str_buf_len_; + void *dbstl_str_buf_; // Stores a sequence, used when ptype is T* + + iterator_type *_DB_STL_itr_; + ptype dbstl_my_value_; + mutable bool _DB_STL_delete_itr_; +}; +#else +}; +#endif // DOXYGEN_CANNOT_SEE_THIS +//@} // Element_wrappers +//@} //dbstl_helper_classes + +// These operators help reading from and writing to iostreams, if the wrapped +// data type has iostream operators. +template<Typename _CharT, Typename _Traits, Typename ddt> +basic_istream<_CharT,_Traits>& +operator>>( basic_istream<_CharT,_Traits> & in, ElementRef<ddt>&p) +{ + in>>(ddt)p; + return in; +} + +template<Typename _CharT, Typename _Traits, Typename ddt> +basic_ostream<_CharT,_Traits>& +operator<<( basic_ostream<_CharT,_Traits> & out, + const ElementRef<ddt>&p) +{ + out<<(ddt)p; + return out; +} + +template<Typename _CharT, Typename _Traits, Typename ddt> +basic_istream<_CharT,_Traits>& +operator>>( basic_istream<_CharT,_Traits> & in, ElementHolder<ddt>&p) +{ + in>>p._DB_STL_value(); + return in; +} + +template<Typename _CharT, Typename _Traits, Typename ddt> +basic_ostream<_CharT,_Traits>& +operator<<( basic_ostream<_CharT,_Traits> & out, + const ElementHolder<ddt>&p) +{ + out<<p._DB_STL_value(); + return out; +} + +template<typename T> +class _exported DbstlSeqWriter +{ +public: + typedef ElementHolder<T *> HolderType; + static void copy_to_holder(HolderType *holder, T *src) + { + size_t i, slen, sql; + + if (src == NULL) { + free(holder->dbstl_str_buf_); + holder->dbstl_str_buf_ = NULL; + holder->dbstl_my_value_ = NULL; + return; + } + if (holder->dbstl_str_buf_len_ > DBSTL_MAX_DATA_BUF_LEN) { + free(holder->dbstl_str_buf_); + holder->dbstl_str_buf_ = NULL; + } + + typedef DbstlElemTraits<T> DM; + typename DM::SequenceCopyFunct seqcpy = + DM::instance()->get_sequence_copy_function(); + typename DM::SequenceLenFunct seqlen = + DM::instance()->get_sequence_len_function(); + typename DM::ElemSizeFunct elemszf = + DM::instance()->get_size_function(); + + assert(seqcpy != NULL && seqlen != NULL); + sql = seqlen(src); + if (elemszf == NULL) + slen = sizeof(T) * (sql + 1); + else + // We don't add the terminating object if it has one. + // So the registered functions should take care of it. + for (slen = 0, i = 0; i < sql; i++) + slen += elemszf(src[i]); + + if (slen > holder->dbstl_str_buf_len_) + holder->dbstl_str_buf_ = DbstlReAlloc( + holder->dbstl_str_buf_, + holder->dbstl_str_buf_len_ = slen); + + seqcpy((T*)holder->dbstl_str_buf_, src, sql); + holder->dbstl_my_value_ = (T*)holder->dbstl_str_buf_; + } +}; + +template<> +class _exported DbstlSeqWriter<char> +{ +public: + typedef ElementHolder<char *> HolderType; + static void copy_to_holder(HolderType *holder, char *src) + { + size_t slen; + + if (src == NULL) { + free(holder->dbstl_str_buf_); + holder->dbstl_str_buf_ = NULL; + holder->dbstl_my_value_ = NULL; + return; + } + if (holder->dbstl_str_buf_len_ > DBSTL_MAX_DATA_BUF_LEN) { + free(holder->dbstl_str_buf_); + holder->dbstl_str_buf_ = NULL; + } + + slen = sizeof(char) * (strlen(src) + 1); + if (slen > holder->dbstl_str_buf_len_) + holder->dbstl_str_buf_ = DbstlReAlloc( + holder->dbstl_str_buf_, + (u_int32_t)(holder->dbstl_str_buf_len_ = slen)); + + strcpy((char*)holder->dbstl_str_buf_, src); + holder->dbstl_my_value_ = (char*)holder->dbstl_str_buf_; + + } +}; + +template<> +class _exported DbstlSeqWriter<wchar_t> +{ +public: + typedef ElementHolder<wchar_t *> HolderType; + static void copy_to_holder(HolderType *holder, wchar_t *src) + { + size_t slen; + + if (src == NULL) { + free(holder->dbstl_str_buf_); + holder->dbstl_str_buf_ = NULL; + holder->dbstl_my_value_ = NULL; + return; + } + if (holder->dbstl_str_buf_len_ > DBSTL_MAX_DATA_BUF_LEN) { + free(holder->dbstl_str_buf_); + holder->dbstl_str_buf_ = NULL; + } + + slen = sizeof(wchar_t) * (wcslen(src) + 1); + if (slen > holder->dbstl_str_buf_len_) + holder->dbstl_str_buf_ = DbstlReAlloc( + holder->dbstl_str_buf_, + holder->dbstl_str_buf_len_ = slen); + + wcscpy((wchar_t*)holder->dbstl_str_buf_, src); + holder->dbstl_my_value_ = (wchar_t*)holder->dbstl_str_buf_; + } +}; +END_NS + +#endif// !_DB_STL_KDPAIR_H diff --git a/stl/dbstl_exception.h b/stl/dbstl_exception.h new file mode 100644 index 0000000..a0ff476 --- /dev/null +++ b/stl/dbstl_exception.h @@ -0,0 +1,257 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#ifndef _DB_STL_EXCEPTION_H +#define _DB_STL_EXCEPTION_H + +#include <cstring> +#include <cstdlib> +#include <cstdio> + +#include <iostream> +#include <exception> + +#include "dbstl_common.h" + +START_NS(dbstl) + +using std::cerr; + +// Internally used only. +void _exported throw_bdb_exception(const char *caller, int err_ret); +#define COPY_CONSTRUCTOR(type) type(const type& t) : DbstlException(t){} + +/** \defgroup Exception_classes_group dbstl exception classes +dbstl throws several types of exceptions on several kinds of errors, the +exception classes form a class hiarachy. First, there is the DbstlException, +which is the base class for all types of dbstl specific concrete exception +classes. +DbstlException inherits from the class DbException of Berkeley DB C++ API. Since +DbException class inherits from C++ STL exception base class std::exception, +you can make use of all Berkeley DB C++ and dbstl API exceptions in the same +way you use the C++ std::exception class. + +Besides exceptions of DbstlException and its subclasses, dbstl may also +throw exceptions of DbException and its subclasses, which happens when a +Berkeley DB call failed. So you should use the same way you catch Berkeley DB +C++ API exceptions when you want to catch exceptions throw by Berkeley DB +operations. + +When an exception occurs, dbstl initialize an local exception object on the +stack and throws the exception object, so you should catch an exception like +this: + +try { + // dbstl operations +} +catch(DbstlException ex){ + // Exception handling + throw ex; // Optionally throw ex again +} + +@{ +*/ + +/// Base class of all dbstl exception classes. It is derived from Berkeley +/// DB C++ API DbException class to maintain consistency with all +/// Berkeley DB exceptions. +/// +class _exported DbstlException : public DbException +{ +public: + explicit DbstlException(const char *msg) : DbException(msg) {} + DbstlException(const char *msg, int err) : DbException(msg, err) {} + DbstlException(const DbstlException&ex) : DbException(ex) {} + explicit DbstlException(int err) : DbException(err) {} + DbstlException(const char *prefix, const char *msg, int err) : + DbException(prefix, msg, err) {} + + const DbstlException& operator=(const DbstlException&exobj) + { + ASSIGNMENT_PREDCOND(exobj) + DbException::operator = + (dynamic_cast<const DbException&>(exobj)); + return exobj; + } + + virtual ~DbstlException() throw(){} +}; + +/// Failed to allocate memory because memory is not enough. +class _exported NotEnoughMemoryException : public DbstlException +{ + size_t failed_size; // The size of the failed allocation. +public: + NotEnoughMemoryException(const char *msg, size_t sz) + : DbstlException(msg) + { + failed_size = sz; + } + + + NotEnoughMemoryException(const NotEnoughMemoryException &ex) + : DbstlException(ex) + { + this->failed_size = ex.failed_size; + } +}; + +/// The iterator has inconsistent status, it is unable to be used any more. +class _exported InvalidIteratorException : public DbstlException +{ +public: + InvalidIteratorException() : DbstlException("Invalid Iterator") + { + } + + explicit InvalidIteratorException(int error_code) : + DbstlException("Invalid Iterator", error_code) + { + } + COPY_CONSTRUCTOR(InvalidIteratorException) +}; + +/// The cursor has inconsistent status, it is unable to be used any more. +class _exported InvalidCursorException : public DbstlException +{ +public: + InvalidCursorException() : DbstlException("Invalid cursor") + { + } + + explicit InvalidCursorException(int error_code) : + DbstlException("Invalid cursor", error_code) + { + } + COPY_CONSTRUCTOR(InvalidCursorException) +}; + +/// The Dbt object has inconsistent status or has no valid data, it is unable +/// to be used any more. +class _exported InvalidDbtException : public DbstlException +{ +public: + InvalidDbtException() : DbstlException("Invalid Dbt object") + { + } + + explicit InvalidDbtException(int error_code) : + DbstlException("Invalid Dbt object", error_code) + { + } + COPY_CONSTRUCTOR(InvalidDbtException) +}; + +/// The assertions inside dbstl failed. The code file name and line number +/// will be passed to the exception object of this class. +class _exported FailedAssertionException : public DbstlException +{ +private: + char *err_msg_; +public: + virtual const char *what() const throw() + { + return err_msg_; + } + + FailedAssertionException(const char *fname, size_t lineno, + const char *msg) : DbstlException(0) + { + u_int32_t sz; + char *str; + + str = (char *)DbstlMalloc(sz = (u_int32_t)(strlen(msg) + + strlen(fname) + 128)); + _snprintf(str, sz, + "In file %s at line %u, %s expression failed", + fname, (unsigned int)lineno, msg); + err_msg_ = str; +#ifdef DEBUG + fprintf(stderr, "%s", str); +#endif + } + + FailedAssertionException(const FailedAssertionException&ex) : + DbstlException(ex) + { + err_msg_ = (char *)DbstlMalloc((u_int32_t) + strlen(ex.err_msg_) + 1); + strcpy(err_msg_, ex.err_msg_); + } + virtual ~FailedAssertionException() throw() + { + free(err_msg_); + } +}; + +/// There is no such key in the database. The key can't not be passed into +/// the exception instance because this class has to be a class template for +/// that to work. +class _exported NoSuchKeyException : public DbstlException +{ +public: + NoSuchKeyException() + : DbstlException("\nNo such key in the container.") + { + } + + COPY_CONSTRUCTOR(NoSuchKeyException) +}; + +/// Some argument of a function is invalid. +class _exported InvalidArgumentException : public DbstlException +{ +public: + explicit InvalidArgumentException(const char *errmsg) : + DbstlException(errmsg) + { +#ifdef DEBUG + cerr<<errmsg; +#endif + } + + InvalidArgumentException(const char *argtype, const char *arg) : + DbstlException(argtype, arg, 0) + { +#ifdef DEBUG + cerr<<"\nInvalid argument exception: "<<argtype<<"\t"<<arg; +#endif + } + + COPY_CONSTRUCTOR(InvalidArgumentException) +}; + +/// The function called is not supported in this class. +class _exported NotSupportedException : public DbstlException +{ +public: + explicit NotSupportedException(const char *str) : DbstlException(str) + { + } + + COPY_CONSTRUCTOR(NotSupportedException) +}; + +/// The function can not be called in this context or in current configurations. +class _exported InvalidFunctionCall : public DbstlException +{ +public: + explicit InvalidFunctionCall(const char *str) : DbstlException(str) + { +#ifdef DEBUG + cerr<<"\nInvalid function call: "<<str; +#endif + } + + COPY_CONSTRUCTOR(InvalidFunctionCall) +}; +/** @}*/ +#undef COPY_CONSTRUCTOR +END_NS + +#endif //_DB_STL_EXCEPTION_H diff --git a/stl/dbstl_inner_utility.h b/stl/dbstl_inner_utility.h new file mode 100644 index 0000000..fcf1431 --- /dev/null +++ b/stl/dbstl_inner_utility.h @@ -0,0 +1,59 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#ifndef _DB_STL_GLOBAL_INNER_OBJECT_ +#define _DB_STL_GLOBAL_INNER_OBJECT_ + +#include "dbstl_common.h" + +START_NS(dbstl) +/* + * This is the interface for all classes that has some global/singleton + * instances that will survive during the entire process lifetime and + * need to be deleted before process exit. Not deleting them won't make + * a difference because they have to be alive when the process is alive, + * they are not memory leaks. However, we will still delete them before + * process exit, to make no memory leak reports by memory leak checkers. + */ +class _exported DbstlGlobalInnerObject +{ +public: + DbstlGlobalInnerObject(){} + virtual ~DbstlGlobalInnerObject(){} + +}; // DbstlGlobalInnerObject + +void _exported register_global_object(DbstlGlobalInnerObject *gio); + +// This class stores the pointer of an object allocated on heap, and when +// an instance of this class is destructed, it deletes that object. +// Any instance of this class can only be created on the heap so that we +// can control when to destruct its instances. It derives from +// DbstlGlobalInnerObject so that we can register pointers to instances of +// this class into ResourceManager, just like other objects which implements +// the DbstlGlobalInnerObject interface. So the ultimate purpose for this +// template is to manage objects which can't implement DbstlGlobalInnerObject +// interface, like objects of Db, DbEnv, etc. +// +template<typename T> +class _exported DbstlHeapObject : public DbstlGlobalInnerObject +{ +private: + typedef DbstlHeapObject<T> self; + T *obj; + + // Only allow creating to heap. + DbstlHeapObject(T *obj1) { obj = obj1; } +public: + static self *instance(T *obj1) { return new self(obj1); } + virtual ~DbstlHeapObject() { delete obj; } +}; // DbstlHeapObject + +END_NS +#endif // !_DB_STL_GLOBAL_INNER_OBJECT_ + diff --git a/stl/dbstl_map.h b/stl/dbstl_map.h new file mode 100644 index 0000000..05d9ed0 --- /dev/null +++ b/stl/dbstl_map.h @@ -0,0 +1,3395 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#ifndef _DB_STL_DB_MAP_H_ +#define _DB_STL_DB_MAP_H_ + +#include <string> + +#include "dbstl_common.h" +#include "dbstl_dbc.h" +#include "dbstl_container.h" +#include "dbstl_resource_manager.h" +#include "dbstl_element_ref.h" +#include "dbstl_base_iterator.h" + +START_NS(dbstl) + +using std::pair; +using std::make_pair; +using std::string; +// Forward declarations, DO NOT delete the default argument values +// because class templates defintions need them. No need for _export here. +// +template <Typename T> +class _DB_STL_set_value; + +template <Typename kdt, Typename ddt, Typename value_type_sub = + ElementRef<ddt> > +class db_map_iterator; + +template <Typename kdt, Typename ddt, Typename value_type_sub = + ElementRef<ddt>, Typename iterator_t = + db_map_iterator<kdt, ddt, value_type_sub> > +class db_map; + +template<Typename kdt, Typename ddt, Typename value_type_sub = + ElementRef<ddt>, Typename iterator_t = + db_map_iterator<kdt, ddt, value_type_sub> > +class db_multimap; + +template <Typename kdt> +class db_set_base_iterator; + +template <Typename kdt, Typename value_type_sub = ElementRef<kdt> > +class db_set_iterator; + +template <Typename kdt, Typename value_type_sub = ElementRef<kdt> > +class db_set; + +template <Typename kdt, Typename value_type_sub = ElementRef<kdt> > +class db_multiset; + +/** \ingroup dbstl_iterators +@{ +\defgroup db_map_iterators Iterator classes for db_map and db_multimap. +db_map has two iterator class templates -- db_map_base_iterator and +db_map_iterator. They are the const iterator class and iterator class for +db_map and db_multimap. db_map_iterator inherits from db_map_base_iterator. + +The two classes have identical behaviors to std::map::const_iterator and +std::map::iterator respectively. Note that the common public member function +behaviors are described in the db_base_iterator section. + +The differences between the two classes are that the db_map_base_iterator can +only be used to read its referenced value, while db_map_iterator 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. +@{ +*/ +////////////////////////////////////////////////////////////////////// +// db_map_base_iterator class definition +// +// This class is a const iterator class for db_map and db_multimap, it can +// be used only to read data under the iterator, can't be used to write. +// +// Iterator const-ness implementation: +// +// const iterators can not update key/data pairs, other than this, +// they can do anything else like non-const iterators, so we define +// db_map_base_iterator to be the const iterator which can only be used +// to read its underlying key/data pair, but not updating them; We +// derive the db_map_iterator from the base iterator to be the +// read-write iterator. We also maintain a "readonly" property in all +// iterators so that users can specify a db_map_iterator to be +// read only. db_map_base_iterator is more efficient to read data then +// db_map_iterator, so read only accesses are strongly recommended to be +// done using a const iterator. +// +template <Typename kdt, Typename ddt, Typename csrddt = ddt> +class _exported db_map_base_iterator : public + db_base_iterator<ddt> +{ +protected: + typedef db_map_base_iterator<kdt, ddt, csrddt> self; + typedef db_base_iterator<ddt> base; + using base::replace_current_key; +public: + typedef kdt key_type; + typedef ddt data_type; + typedef pair<kdt, ddt> value_type; + // Not used in this class, but required to satisfy + // db_reverse_iterator type extraction. + typedef ptrdiff_t difference_type; + typedef difference_type distance_type; + typedef value_type& reference; + typedef value_type* pointer; + typedef value_type value_type_wrap; + // We have to use standard iterator tags to match the parameter + // list of stl internal functions, we can't use our own tag + // classes, so we don't write tag classes in dbstl. + // + typedef std::bidirectional_iterator_tag iterator_category; + + //////////////////////////////////////////////////////////////////// + // + // Begin public constructors and destructor. + /// @name Constructors and destructor + /// Do not create iterators directly using these constructors, but + /// call db_map::begin or db_multimap_begin to get instances of + /// this class. + /// \sa db_map::begin() db_multimap::begin() + //@{ + /// Copy constructor. + /// \param vi The other iterator of the same type to initialize this. + db_map_base_iterator(const self& vi) + : db_base_iterator<ddt>(vi) + { + // Lazy-dup another cursor, cursor to iterator mapping + // is 1 to 1. + pcsr_ = vi.pcsr_; + curpair_base_.first = vi.curpair_base_.first; + curpair_base_.second = vi.curpair_base_.second; + } + + /// Base copy constructor. + /// \param vi Initialize from a base class iterator. + db_map_base_iterator(const base& vi) : base(vi), + pcsr_(new cursor_type(vi.get_bulk_retrieval(), + vi.is_rmw(), vi.is_directdb_get())) + { + + } + + /// Constructor. + /// \param powner The container which creates this iterator. + /// \param b_bulk_retrieval The bulk read buffer size. 0 means + /// bulk read disabled. + /// \param rmw Whether set DB_RMW flag in underlying cursor. + /// \param directdbget Whether do direct database get rather than + /// using key/data values cached in the iterator whenever read. + /// \param readonly Whether open a read only cursor. Only effective + /// when using Berkeley DB Concurrent Data Store. + explicit db_map_base_iterator(db_container*powner, + u_int32_t b_bulk_retrieval = 0, bool rmw = false, + bool directdbget = true, bool readonly = false) + : db_base_iterator<ddt>( + powner, directdbget, readonly, b_bulk_retrieval, rmw), + pcsr_(new cursor_type(b_bulk_retrieval, rmw, directdbget)) + { + } + + /// Default constructor, dose not create the cursor for now. + db_map_base_iterator() + { + } + + // Use virtual because ElementRef<> uses a db_base_iterator* pointer + // to refer to the iterator, and also use "dead_" flag to avoid + // multiple calls to the same destructor by ~ElementRef<>(). + /// Destructor. + virtual ~db_map_base_iterator() + { + this->dead_ = true; + if (pcsr_) + pcsr_->close(); + } + //@} + + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // Begin functions that shift iterator position. + // + // Do not throw exceptions here because it is likely and normal + // to iterate to the "end itrerator". + // + /// @name Iterator increment movement functions. + /// The two functions moves the iterator one element backward, so that + /// the element it sits on has a bigger key. The btree/hash key + /// comparison routine determines which key is greater. + /// Use ++iter rather than iter++ where possible to avoid two useless + /// iterator copy constructions. + //@{ + /// Pre-increment. + /// \return This iterator after incremented. + inline self& operator++() + { + next(); + + return *this; + } + + /// Post-increment. + /// \return Another iterator having the old value of this iterator. + inline self operator++(int) + { + self itr = *this; + + next(); + + return itr; + } + //@} + + /// @name Iterator decrement movement functions. + /// The two functions moves the iterator one element forward, so that + /// the element it sits on has a smaller key. The btree/hash key + /// comparison routine determines which key is greater. + /// Use --iter rather than iter-- where possible to avoid two useless + /// iterator copy constructions. + //@{ + /// Pre-decrement. + /// \return This iterator after decremented. + inline self& operator--() + { + prev(); + + return *this; + } + + /// Post-decrement. + /// \return Another iterator having the old value of this iterator. + self operator--(int) + { + self itr = *this; + prev(); + + return itr; + } + //@} + + /// 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 assignment. + /// \return The reference of itr. + /// \sa db_base_iterator::operator=(const self&) + // We will duplicate the Dbc cursor here. + inline const self& operator=(const self&itr) + { + ASSIGNMENT_PREDCOND(itr) + base::operator=(itr); + + curpair_base_.first = itr.curpair_base_.first; + curpair_base_.second = itr.curpair_base_.second; + if (pcsr_) + pcsr_->close(); + pcsr_ = itr.pcsr_; + + return itr; + } + + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // Begin iterator comparison functions. + // + /// \name Compare operators. + /// Only equal comparison is supported. + //@{ + /// Compare two iterators. + /// Two iterators compare equal when they are both invalid or + /// both valid and underlying cursor compare equal(i.e. sitting on the + /// same key/data pair). + // + // Note that the iterator itr or this iterator may be an invalid + // one, i.e. its this->itr_status_ is INVALID_ITERATOR_POSITION. + // We do not distinguish between end and rend iterators although + // we are able to do so, because they are never compared together. + /// Equal comparison operator. + /// \param itr The iterator to compare against. + /// \return Returns true if equal, 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)) || + ((itr.itr_status_ == this->itr_status_) && + (pcsr_->compare((itr.pcsr_.base_ptr())) == 0))) + return true; + return false; + + } + + /// Unequal comparison operator. + /// \param itr The iterator to compare against. + /// \return Returns false if equal, true otherwise. + /// \sa bool operator==(const self&itr) const + inline bool operator!=(const self&itr) const + { + return !(*this == itr) ; + } + //@} + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // Begin functions that retrieve values from the iterator. + // + // curpair_base_ is always kept updated on iterator movement, but if + // directdb_get_ is true, curpair_base_ is also updated here before + // making use of the value it references. + // Even if this iterator is invalid, this call is allowed, the + // default value of type T is returned. + // + // Note that the returned reference can only be used to read data, + // can't be used to update data. + /// \name Functions that retrieve values from the iterator. + //@{ + /// Dereference operator. + /// Return the reference to the cached data element, which is an + /// pair<Key_type, T>. You can only read its referenced data via + /// this iterator but can not update it. + /// \return Current data element reference object, i.e. ElementHolder + /// or ElementRef object. + inline reference operator*() const + { + + if (this->directdb_get_) { + csrddt d; + pcsr_->get_current_key_data(curpair_base_.first, d); + assign_second0(curpair_base_, d); + } + // Returning reference, no copy construction. + return curpair_base_; + } + + // curpair_base_ is always kept updated on iterator movement, but if + // directdb_get_ is true, curpair_base_ is also updated here before + // making use of the value it references. + // Even if this iterator is invalid, this call is allowed, the + // default value of type T is returned. + // + // Note that the returned reference can only be used to read data, + // can't be used to update data. + /// Arrow operator. + /// Return the pointer to the cached data element, which is an + /// pair<Key_type, T>. You can only read its referenced data via + /// this iterator but can not update it. + /// \return Current data element reference object's address, i.e. + /// address of ElementHolder or ElementRef object. + inline pointer operator->() const + { + + if (this->directdb_get_) { + csrddt d; + pcsr_->get_current_key_data(curpair_base_.first, d); + assign_second0(curpair_base_, d); + } + + return &curpair_base_; + } + //@} + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // Begin dbstl specific functions. + // + // Refresh the underlying cursor's current data and this object's + // curpair_base_. It need to be called only if directdb_get is + // disabled, and other iterators updated + // the key/data pair this iterator points to and we are about to use + // this iterator to access that key/data pair. + // If direct db get is enabled, this method never needs to be called. + /// @name 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) const + { + csrddt d; + + if (from_db && !this->directdb_get_) + pcsr_->update_current_key_data_from_db( + DbCursorBase::SKIP_NONE); + pcsr_->get_current_key_data(curpair_base_.first, d); + assign_second0(curpair_base_, d); + + return 0; + } + + // By calling this function, users can choose to close the underlying + // cursor before iterator destruction to get better performance + // potentially. + /// \brief Close underlying Berkeley DB cursor of this iterator. + /// \sa db_base_iterator::close_cursor() const + inline void close_cursor() const + { + if (pcsr_) + pcsr_->close(); + } + + /// Iterator movement function. + /// Move this iterator to the specified key k, by default moves + /// exactly to k, and update cached data element, you can + /// also specify DB_SET_RANGE, to move to the biggest key smaller + /// than k. The btree/hash key comparison routine determines which + /// key is bigger. When the iterator is on a multiple container, + /// move_to will move itself to the first key/data pair of the + /// identical keys. + /// \param k The target key value to move to. + /// \param flag Flags available: DB_SET(default) or DB_SET_RANGE. + /// DB_SET will move this iterator exactly at k; DB_SET_RANGE moves + /// this iterator to k or the smallest key greater than k. If fail + /// to find such a key, this iterator will become invalid. + /// \return 0 if succeed; non-0 otherwise, and this iterator becomes + /// invalid. Call db_strerror with the return value to get the error + /// message. + inline int move_to(const kdt& k, int flag = DB_SET) const + { + int ret; + // Use tmpk2 to avoid k being modified. + kdt tmpk2 = k; + + this->itr_status_ = (ret = pcsr_->move_to(tmpk2, flag)); + if (ret != 0) { + this->inval_pos_type_ = base::IPT_UNSET; + return ret; + } + + refresh(); + + return ret; + } + + /// Modify bulk buffer size. + /// Bulk read is enabled when creating an + /// iterator, so users 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 ) + bool set_bulk_buffer(u_int32_t sz) + { + bool ret = this->pcsr_->set_bulk_buffer(sz); + if (ret) + this->bulk_retrieval_ = + this->pcsr_->get_bulk_bufsize(); + 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: + + // The cursor_type is used to directly return the pair object, + // rather than a reference to it + typedef DbCursor<kdt, csrddt> cursor_type; + + friend class db_map_iterator<kdt, ddt, ElementRef<ddt> >; + friend class db_map_iterator<kdt, ddt, ElementHolder<ddt> >; + // Use friend classes to hide internal members from users. + friend class db_map<kdt, ddt, ElementHolder<ddt> >; + friend class db_map<kdt, ddt, ElementHolder<kdt>, + db_set_iterator<kdt, ElementHolder<kdt> > >; + friend class db_map<kdt, _DB_STL_set_value<kdt>, ElementHolder<kdt>, + db_set_iterator<kdt, ElementHolder<kdt> > >; + + friend class db_set<kdt, ElementHolder<kdt> >; + friend class db_set_iterator<kdt, ElementHolder<kdt> >; + friend class db_multiset<kdt, ElementHolder<kdt> >; + friend class db_multimap<kdt, ddt, ElementHolder<ddt> >; + friend class db_multimap<kdt, _DB_STL_set_value<kdt>, + ElementHolder<kdt>, db_set_iterator<kdt, ElementHolder<kdt> > >; + + friend class db_map<kdt, ddt, ElementRef<ddt> >; + friend class db_map<kdt, _DB_STL_set_value<kdt>, ElementRef<kdt>, + db_set_iterator<kdt, ElementRef<kdt> > >; + + friend class db_set<kdt, ElementRef<kdt> >; + friend class db_set_iterator<kdt, ElementRef<kdt> >; + friend class db_multiset<kdt, ElementRef<kdt> >; + friend class db_multimap<kdt, ddt, ElementRef<ddt> >; + friend class db_multimap<kdt, _DB_STL_set_value<kdt>, ElementRef<kdt>, + db_set_iterator<kdt, ElementRef<kdt> > >; + + + //////////////////////////////////////////////////////////////////// + // Begin db_map_base_iterator data members. + // + // Cursor of this iterator, note that each db_map_base_iterator has a + // unique DbCursor, not shared with any other iterator, and when copy + // constructing or assigning, the cursor is duplicated + // when it is actually used to access db. + // + mutable LazyDupCursor<DbCursor<kdt, csrddt> > pcsr_; + + // In order for std::map style itrerator to work, we need a pair + // here to store the key-value pair this iterator currently points + // to in the db_map. + // + // curpair_base_ is always kept updated on every cursor/iterator + // movement and initialized to point to the first key-value pair when + // db_map<>::begin() is called. + // + mutable value_type curpair_base_; + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // Begin internal helper functions. + // + // Open the iterator and its cursor. + // + void open() const + { + u_int32_t oflags = 0, coflags = 0; + int ret; + Db *pdb = this->owner_->get_db_handle(); + DbEnv *penv = pdb->get_env(); + + coflags = this->owner_->get_cursor_open_flags(); + assert(this->owner_ != NULL); + if (!this->read_only_ && penv != NULL) { + BDBOP((penv->get_open_flags(&oflags)), ret); + if ((oflags & DB_INIT_CDB) != 0) + this->owner_->set_cursor_open_flags(coflags |= + DB_WRITECURSOR); + } + if (!pcsr_) + pcsr_.set_cursor(new DbCursor<kdt, csrddt>( + this->bulk_retrieval_, + this->rmw_csr_, this->directdb_get_)); + this->itr_status_ = pcsr_->open((db_container*)this->owner_, + coflags); + + } + + // Move this iterator as well as the underlying Dbc* cursor to + // first element and update cur_pair_. + // + int first() const + { + + assert(this->owner_ != NULL); + this->itr_status_ = pcsr_->first(); + if (this->itr_status_ == 0) + refresh(); + else + this->inval_pos_type_ = base::IPT_UNSET; + + return this->itr_status_; + + } + + // Move this iterator as well as the underlying Dbc* cursor + // to last effective(valid) element and update cur_pair_. + // + int last() const + { + + assert(this->owner_ != NULL); + this->itr_status_ = pcsr_->last(); + if (this->itr_status_ == 0) + refresh(); + else + this->inval_pos_type_ = base::IPT_UNSET; + + return this->itr_status_; + } + + // Move this iterator as well as the underlying Dbc* cursor + // to next element, then update its position flags and cur_pair_. + // + int next(int flags = DB_NEXT) const + { + + assert(this->owner_ != NULL); + + if (this->itr_status_ == INVALID_ITERATOR_POSITION) { + if (this->inval_pos_type_ == base::IPT_BEFORE_FIRST) { + // This rend itr must have an non-NULL owner. + open(); + // rend itr can go back to first element. + this->itr_status_ = first(); + } else if (this->inval_pos_type_ == base::IPT_UNSET) { + THROW0(InvalidIteratorException); + } + // Else, return itr_status_ in last line. + } else { + + this->itr_status_ = pcsr_->next(flags); + if (this->itr_status_ == 0) + refresh(); + else + this->inval_pos_type_ = base::IPT_AFTER_LAST; + + } + + return this->itr_status_; + } + + // Move this iterator as well as the underlying Dbc* cursor + // to previous element. + // + int prev(int flags = DB_PREV) const + { + + assert(this->owner_ != NULL); + if (this->itr_status_ == INVALID_ITERATOR_POSITION) { + if (this->inval_pos_type_ == base::IPT_AFTER_LAST) { + // This rend itr must have an non-NULL owner. + open(); + // end itr can go back to last element. + this->itr_status_ = last(); + } else if (this->inval_pos_type_ == base::IPT_UNSET) { + THROW0(InvalidIteratorException); + } + // Else, return itr stat in last line. + } else { + this->itr_status_ = pcsr_->prev(flags); + if (this->itr_status_ == 0) + refresh(); + else + this->inval_pos_type_ = base::IPT_BEFORE_FIRST; + + } + + return this->itr_status_; + } + + void set_curpair_base(const kdt& k, const csrddt &d) const + { + curpair_base_.first = k; + assign_second0(curpair_base_, d); + } + + //////////////////////////////////////////////////////////////////// + +protected: // Do not remove this line, otherwise assign_second0 may be public. +#ifndef DOXYGEN_CANNOT_SEE_THIS +#if NO_MEMBER_FUNCTION_PARTIAL_SPECIALIZATION +};// end of db_map_base_iterator<> +template <Typename kdt, Typename datadt, Typename ddt> +void assign_second0(pair<kdt, ddt>& v, const datadt& d) +{ + v.second = d; +} + +template<Typename kdt, Typename ddt> +void assign_second0(pair<kdt, ddt> &v, + const _DB_STL_set_value<kdt>& + /* d unused, use v.first to assign v.second */) +{ + v.second = v.first; +} +#else + +template <Typename datadt> +inline void assign_second0(value_type& v, const datadt& d) const +{ + v.second = d; +} + +template<> +inline void +assign_second0(value_type &v, const _DB_STL_set_value<kdt>& + /* d unused, use v.first to assign v.second */) const +{ + v.second = v.first; +} + +};// end of db_map_base_iterator<> + +#endif + +#else +}; +#endif // DOXYGEN_CANNOT_SEE_THIS +//@} // db_map_iterators +//@} // dbstl_iterators + + +#if NO_MEMBER_FUNCTION_PARTIAL_SPECIALIZATION +template <Typename kdt, Typename datadt, Typename value_type_sub> +void assign_second0(pair<kdt, value_type_sub>& v, const datadt& d) ; +#endif + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +// +// db_map_iterator class template definition +// +// db_map_iterator is the iterator class template for db_map and +// db_multimap, it is also the base class for db_set_iterator. It can be +// used to both read and write the database. +// +// Template parameters info: +// kdt is "key data type", ddt is "data data type", value_type_sub is +// either ElementRef<ddt> (by default, in this case ElementRef inherits +// from ddt, so ddt must not be a primitive type) or ElementHolder<ddt>, +// in this case ElementHolder has a data member of type ddt, so suitable for +// primitive types, but don't apply it to classes otherwise you can't access +// members like this : *iterator.member = value. +// +/// \ingroup dbstl_iterators +//@{ +/// \ingroup db_map_iterators +//@{ +template <Typename kdt, Typename ddt, Typename value_type_sub> +class _exported db_map_iterator : public + db_map_base_iterator<kdt, typename value_type_sub::content_type, ddt> +{ +protected: + typedef db_map_iterator<kdt, ddt, value_type_sub> self; + typedef typename value_type_sub::content_type realddt; + using db_base_iterator<typename value_type_sub::content_type>:: + replace_current_key; +public: + typedef kdt key_type; + typedef ddt data_type; + typedef pair<kdt, ddt> value_type; + typedef pair<kdt, value_type_sub> value_type_wrap; + // Not used in this class, but required to satisfy + // db_reverse_iterator type extraction. + typedef ptrdiff_t difference_type; + typedef difference_type distance_type; + typedef value_type_wrap& reference; + typedef value_type_wrap* pointer; + + // We have to use standard iterator tags to match the parameter + // list of stl internal functions, we can't use our own tag + // classes, so we don't write tag classes in dbstl. + // + typedef std::bidirectional_iterator_tag iterator_category; + + // Refresh the underlying cursor's current data and this object's + // curpair_. It need to be called only if other iterators updated the + // key/data pair this iterator points to and we are about to use + // this iterator to access that key/data pair. If direct db get is + // enabled, this method never needs to be called. + /// \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) const + { + kdt k; + ddt d; + + if (from_db && !this->directdb_get_) + this->pcsr_->update_current_key_data_from_db( + DbCursorBase::SKIP_NONE); + this->pcsr_->get_current_key_data(k, d); + curpair_.first = k; + assign_second(curpair_, d); + this->set_curpair_base(k, d); + + return 0; + } + + //////////////////////////////////////////////////////////////// + // Begin constructors and destructor definitions. + /// \name Constructors and destructor + /// Do not create iterators directly using these constructors, but + /// call db_map::begin or db_multimap_begin to get instances of + /// this class. + /// \sa db_map::begin() db_multimap::begin() + //@{ + /// Copy constructor. + /// \param vi The other iterator of the same type to initialize this. + db_map_iterator(const db_map_iterator<kdt, ddt, value_type_sub>& vi) + : db_map_base_iterator<kdt, realddt, ddt>(vi) + { + // Lazy-dup another cursor, cursor to iterator mapping + // is 1 to 1. + curpair_.first = vi.curpair_.first; + curpair_.second._DB_STL_CopyData(vi.curpair_.second); + curpair_.second._DB_STL_SetIterator(this); + } + + /// Base copy constructor. + /// \param vi Initialize from a base class iterator. + db_map_iterator(const db_map_base_iterator<kdt, realddt, ddt>& vi) : + db_map_base_iterator<kdt, realddt, ddt>(vi) + + { + curpair_.second._DB_STL_SetIterator(this); + curpair_.first = vi->first; + curpair_.second._DB_STL_CopyData(vi->second); + } + + /// Constructor. + /// \param powner The container which creates this iterator. + /// \param b_bulk_retrieval The bulk read buffer size. 0 means + /// bulk read disabled. + /// \param brmw Whether set DB_RMW flag in underlying cursor. + /// \param directdbget Whether do direct database get rather than + /// using key/data values cached in the iterator whenever read. + /// \param b_read_only Whether open a read only cursor. Only effective + /// when using Berkeley DB Concurrent Data Store. + explicit db_map_iterator(db_container*powner, + u_int32_t b_bulk_retrieval = 0, bool brmw = false, + bool directdbget = true, bool b_read_only = false) + : db_map_base_iterator<kdt, realddt, ddt> + (powner, b_bulk_retrieval, brmw, directdbget, b_read_only) + { + curpair_.second._DB_STL_SetIterator(this); + } + + /// Default constructor, dose not create the cursor for now. + db_map_iterator() : db_map_base_iterator<kdt, realddt, ddt>() + { + curpair_.second._DB_STL_SetIterator(this); + } + + // Use virtual because ElementRef<> uses a db_base_iterator* pointer + // to refer to the iterator, and also use "dead_" flag to avoid + // multiple call to the same destructor by ~ElementRef<>(). + /// Destructor. + virtual ~db_map_iterator() + { + // Required here though set in base destructor too. + this->dead_ = true; + } + //@} + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // Begin functions that shift iterator position. + // + // Do not throw exceptions here because it is likely and normal + // to iterate to the "end itrerator". + /// \name Iterator movement operators. + //@{ + /// Pre-increment \sa db_map_base_iterator::operator++() + /// \return This iterator after incremented. + inline self& operator++() + { + this->next(); + + return *this; + } + + /// Post-increment \sa db_map_base_iterator::operator++(int) + /// \return Another iterator having the old value of this iterator. + inline self operator++(int) + { + self itr = *this; + + this->next(); + + return itr; + } + + /// Pre-decrement \sa db_map_base_iterator::operator--() + /// \return This iterator after decremented. + inline self& operator--() + { + this->prev(); + + return *this; + } + + /// Post-decrement \sa db_map_base_iterator::operator--(int) + /// \return Another iterator having the old value of this iterator. + self operator--(int) + { + self itr = *this; + this->prev(); + + return itr; + } + //@} + // Assignment operator, we will duplicate the Dbc cursor here. + /// 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 assignment. + /// \return The reference of itr. + /// \sa db_base_iterator::operator=(const self&) + inline const self& operator=(const self&itr) + { + ASSIGNMENT_PREDCOND(itr) + base::operator=(itr); + + curpair_.first = itr.curpair_.first; + + // Only copy data from itr.curpair_ into curpair_, + // don't store into db. Note that we can not assign + // itr.curpair_ to curpair_ simply by curpair_ = itr.curpair_, + // otherwise, ElementRef<>::operator= is called, which will + // update the data element referenced by this iterator. + // + curpair_.second._DB_STL_CopyData(itr.curpair_.second); + + return itr; + } + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // Begin functions that retrieve values from the iterator. + // + // curpair_base_ is always kept updated on iterator movement, but if + // directdb_get_ is true, curpair_base_ is also updated here before + // making use of the value it references. + // Even if this iterator is invalid, this call is allowed, the + // default value of type T is returned. + // + /// \name Functions that retrieve values from the iterator. + //@{ + /// Dereference operator. + /// Return the reference to the cached data element, which is an + /// pair<Key_type, ElementRef<T> > object if T is a class type or an + /// pair<Key_type, ElementHolder<T> > object if T is a C++ primitive + /// data type. + /// \return Current data element reference object, i.e. ElementHolder + /// or ElementRef object. + inline reference operator*() const + { + + if (this->directdb_get_) { + ddt d; + this->pcsr_->get_current_key_data(curpair_.first, d); + assign_second(curpair_, d); + } + + return curpair_;// returning reference, no copy construction + } + + // curpair_base_ is always kept updated on iterator movement, but if + // directdb_get_ is true, curpair_base_ is also updated here before + // making use of the value it references. + // Even if this iterator is invalid, this call is allowed, the + // default value of type T is returned. + /// Arrow operator. + /// Return the pointer to the cached data element, which is an + /// pair<Key_type, ElementRef<T> > object if T is a class type or an + /// pair<Key_type, ElementHolder<T> > object if T is a C++ primitive + /// data type. + /// \return Current data element reference object's address, i.e. + /// address of ElementHolder or ElementRef object. + inline pointer operator->() const + { + + if (this->directdb_get_) { + ddt d; + this->pcsr_->get_current_key_data(curpair_.first, d); + assign_second(curpair_, d); + } + return &curpair_; + } + //@} + //////////////////////////////////////////////////////////////////// + +//@} // db_map_iterators +//@} // dbstl_iterators +protected: + // The cursor_type is used to directly return the pair object, + // rather than a reference to it. + typedef DbCursor<kdt, ddt> cursor_type; + typedef db_map_base_iterator<kdt, realddt, ddt> base; + typedef db_map_base_iterator<kdt, realddt> const_version; + + // Use friend classes to hide internal members from users. + friend class db_map<kdt, ddt, value_type_sub>; + friend class db_map<kdt, ddt, value_type_sub, + db_set_iterator<kdt, value_type_sub> >; + friend class db_set<kdt, value_type_sub>; + friend class db_set_iterator<kdt, value_type_sub>; + friend class db_multiset<kdt, value_type_sub>; + friend class db_multimap<kdt, ddt, value_type_sub>; + friend class db_multimap<kdt, _DB_STL_set_value<kdt>, value_type_sub, + db_set_iterator<kdt, value_type_sub > >; + + //////////////////////////////////////////////////////////////// + // Begin db_map_iterator data members. + // + // In order for std::map style itrerator to work, we need a pair + // here to store the key-value pair this iterator currently points + // to in the db_map. + // + // curpair_ is always kept updated on every cursor/iterator movement + // and initialized to point to the first key-value pair when + // db_map<>::begin() is called. + // + mutable value_type_wrap curpair_; + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin internal helper functions. + // + // Called by ElementRef<> object when this iterator belongs to the + // object---The only situation is in db_container::operator[] which + // has to return an ElementRef/Holder object A, and its iterator has + // to survive until A is destructed. + virtual void delete_me() const + { + if (!this->dead_) + delete this; + } + + // Duplicate this iterator. + virtual self* dup_itr() const + { + self *itr = new self(*this); + // The curpair_ of itr does not delete itr, the independent + // one does. + //itr->curpair_.second._DB_STL_SetDelItr(); + return itr; + } + + // Replace the current key/data pair's data pointed to by this + // iterator's underlying Dbc* cursor with the parameter d. + // + virtual int replace_current( + const typename value_type_sub::content_type& d) + { + int ret; + + if (this->read_only_) { + THROW(InvalidFunctionCall, ( +"db_map_iterator<>::replace_current can't be called via a read only iterator")); + } + ret = this->pcsr_->replace(d); + + return ret; + } + + // Used by set iterator to store another different key : + // remove the previous one then insert the new one. + // It has to be defined in this class because db_set_iterator + // inherits from db_map_iterator but we have no polymorphism when + // using stl because the object are always used rather the + // pointer/reference. + // + virtual int replace_current_key(const kdt& k) + { + int ret; + + if (this->read_only_) { + THROW(InvalidFunctionCall, ( +"db_map_iterator<>::replace_current_key can't be called via a read only iterator")); + } + ret = this->pcsr_->replace_key(k); + + return ret; + } + + //////////////////////////////////////////////////////////////// + + +protected: // Do not remove this line, otherwise assign_second may be public. +#ifndef DOXYGEN_CANNOT_SEE_THIS +#if NO_MEMBER_FUNCTION_PARTIAL_SPECIALIZATION +};// end of db_map_iterator<> + +template <Typename kdt, Typename datadt, Typename value_type_sub> +void assign_second(pair<kdt, value_type_sub>& v, const datadt& d) +{ + v.second._DB_STL_CopyData(d); +} + +template<Typename kdt, Typename value_type_sub> +void assign_second(pair<kdt, value_type_sub> &v, + const _DB_STL_set_value<kdt>& + /* d unused, use v.first to assign v.second */) +{ + v.second._DB_STL_CopyData(v.first); +} +#else + +template <Typename datadt> +inline void assign_second(value_type_wrap& v, const datadt& d) const +{ + v.second._DB_STL_CopyData(d); +} + +template<> +inline void +assign_second(value_type_wrap &v, const _DB_STL_set_value<kdt>& + /* d unused, use v.first to assign v.second */) const +{ + v.second._DB_STL_CopyData(v.first); +} + +};// end of db_map_iterator<> + +#endif +#else +}; +#endif // DOXYGEN_CANNOT_SEE_THIS +u_int32_t hash_default(Db *dbp, const void *key, u_int32_t len); + +////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// db_map container class definition +/// \ingroup dbstl_containers +//@{ +/// db_map has identical methods to std::map and the semantics for each +/// method is identical to its std::map counterpart, except that it stores data +/// into underlying Berkeley DB btree or hash database. Passing a database +/// handle of btree or hash type creates a db_map equivalent to std::map and +/// std::hashmap respectively. +/// Database(dbp) and environment(penv) handle requirement(applies to all +/// constructors in this class template): +/// 0. The dbp is opened inside the penv environment. Either one of the two +/// handles can be NULL. If dbp is NULL, an anonymous database is created +/// by dbstl. +/// 1. Database type of dbp should be DB_BTREE or DB_HASH. +/// 2. No DB_DUP or DB_DUPSORT flag set in dbp. +/// 3. No DB_RECNUM flag set in dbp. +/// 4. No DB_TRUNCATE specified in dbp's database open flags. +/// 5. DB_THREAD must be set if you are sharing the dbp across +/// multiple threads directly, or indirectly by sharing the container object +/// across multiple threads. +/// \param kdt The key data type. +/// \param ddt The data data type. db_map stores key/data pairs. +/// \param value_type_sub Do not specify anything if ddt type is a +/// class/struct type; Otherwise, specify ElementHolder<ddt> to it. +/// \param iterator_t Never specify anything to this type parameter. It is +/// only used internally. +/// \sa db_container db_container(Db*, DbEnv*) db_container(const db_container&) +template <Typename kdt, Typename ddt, Typename value_type_sub, + Typename iterator_t> +class _exported db_map : public db_container +{ + +public: + // iterator_t is default argument, see forward declaration at the + // head of this file + typedef iterator_t iterator; + typedef typename iterator::const_version const_iterator; + typedef db_reverse_iterator<iterator, const_iterator> reverse_iterator; + typedef db_reverse_iterator<const_iterator, iterator> + const_reverse_iterator; + typedef kdt key_type; + typedef ddt data_type; + typedef value_type_sub data_type_wrap; + typedef pair<kdt, ddt> value_type; + typedef pair<kdt, value_type_sub> value_type_wrap; + typedef const value_type const_value_type; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + // The following three types are not used in db_map, but we define + // them to conform to stl specifications. + typedef value_type_wrap& reference; + typedef const value_type& const_reference; + typedef value_type_wrap* pointer; +protected: + typedef db_map<kdt, ddt, value_type_sub, iterator> self; + typedef typename value_type_sub::content_type realddt; + + // This constructor is for db_multimap's constructors to call, + // because other constructors of this class will verify db handles + // and create one if needed. We need a special one that don't do + // anything. The BulkRetrievalOption is randomly picked, no special + // implications at all. + db_map(BulkRetrievalOption& arg){ delete &arg; } +public: + //////////////////////////////////////////////////////////////// + // Begin inner class definitions. + // + // key_compare class definition, it is defined as an inner class, + // using underlying btree/hash db's compare function + // + class key_compare + { + private: + Db*pdb; + public: + key_compare(Db*pdb1) + { + pdb = pdb1; + } + bool operator()(const kdt& k1, const kdt& k2) const + { + return compare_keys(pdb, k1, k2); + } + + }; // key_compare class definition + + // value_compare class definition, it is defined as an inner class, + // using key_compare class to do comparison. + // + // The difference between key_compare and value_compare is the + // parameter its operator() function accepts, see the function + // signature. + // + class value_compare + { + key_compare kc; + public: + value_compare(Db*pdb) : kc(pdb) + { + + } + + bool operator()(const value_type& v1, + const value_type& v2) const + { + + return kc(v1.first, v2.first); + } + + }; // value_compare class definition + + class hasher + { + private: + Db*pdb; + public: + hasher(Db*db){pdb = db;} + size_t operator()(const kdt&k) const + { + DBTYPE dbtype; + int ret; + + assert(pdb != NULL); + ret = pdb->get_type(&dbtype); + assert(ret == 0); + if (dbtype != DB_HASH) { + THROW(InvalidFunctionCall, ( + "db_map<>::hasher")); + } + h_hash_fcn_t hash = NULL; + BDBOP(pdb->get_h_hash(&hash), ret); + if (hash == NULL) + hash = hash_default; + return hash(pdb, &k, sizeof(k)); + } + }; // hasher + + class key_equal + { + private: + Db*pdb; + public: + key_equal(Db*db){pdb = db;} + bool operator()(const kdt& kk1, const kdt&kk2) const + { + DBTYPE dbtype; + kdt k1 = kk1, k2 = kk2; + int ret; + + dbstl_assert(pdb != NULL); + ret = pdb->get_type(&dbtype); + dbstl_assert(ret == 0); + if (dbtype != DB_HASH) { + THROW(InvalidFunctionCall, ( + "db_map<>::key_equal")); + } + + db_compare_fcn_t comp = NULL; + BDBOP(pdb->get_h_compare(&comp), ret); + if (comp == NULL) + return memcmp(&kk1, &kk2, sizeof(kdt)) == 0; + Dbt kd1(&k1, sizeof(k1)), kd2(&k2, sizeof(k2)); + + return comp(pdb, &kd1, &kd2) == 0; + + + } + + };// key_equal + //////////////////////////////////////////////////////////////// + + /// Function to get key compare functor. + /// Used when this container is a hash_map, hash_multimap, + /// hash_set or hash_multiset equivalent. + /// \return key_equal type of compare functor. + /// \sa http://www.sgi.com/tech/stl/hash_map.html + inline key_equal key_eq() const + { + key_equal ke(this->get_db_handle()); + return ke; + } + + /// Function to get hash key generating functor. + /// Used when this container is a hash_map, hash_multimap, + /// hash_set or hash_multiset equivalent. + /// \return The hash key generating functor. + /// \sa http://www.sgi.com/tech/stl/hash_map.html + inline hasher hash_funct() const + { + hasher h(this->get_db_handle()); + return h; + + } + + /// Function to get value compare functor. Used when this container + /// is a std::map, std::multimap, std::set or std::multiset equivalent. + /// \return The value compare functor. + /// \sa http://www.cplusplus.com/reference/stl/map/value_comp/ + inline value_compare value_comp() const + { + value_compare vc(this->get_db_handle()); + return vc; + } + + /// Function to get key compare functor. Used when this container + /// is a std::map, std::multimap, std::set or std::multiset equivalent. + /// \return The key compare functor. + /// \sa http://www.cplusplus.com/reference/stl/map/key_comp/ + inline key_compare key_comp() const + { + key_compare kc(this->get_db_handle()); + return kc; + } + + //////////////////////////////////////////////////////////////// + // Begin constructors and destructor definitions. + /// \name Constructors and destructor + //@{ + // We don't need the equal compare or allocator here, user need to + // call Db::set_bt_compare or Db::set_h_compare to set comparison + // function. + /// Create a std::map/hash_map equivalent associative container. + /// See the handle requirement in class details to pass correct + /// database/environment handles. + /// \param dbp The database handle. + /// \param envp The database environment handle. + /// \sa db_container(Db*, DbEnv*) + explicit db_map(Db *dbp = NULL, DbEnv* envp = NULL) : + db_container(dbp, envp) + { + const char *errmsg; + + this->open_db_handles(dbp, envp, DB_BTREE, + DB_CREATE | DB_THREAD, 0); + + if ((errmsg = verify_config(dbp, envp)) != NULL) { + THROW(InvalidArgumentException, ("Db*", errmsg)); + } + this->set_db_handle_int(dbp, envp); + } + + /// Iteration constructor. Iterates between first and last, + /// setting a copy of each of the sequence of elements as the + /// content of the container object. + /// Create a std::map/hash_map equivalent associative container. + /// Insert a range of elements into the database. The range is + /// [first, last), which contains elements that can + /// be converted to type ddt automatically. + /// See the handle requirement in class details to pass correct + /// database/environment handles. + /// This function supports auto-commit. + /// \param dbp The database handle. + /// \param envp The database environment handle. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + /// \sa db_container(Db*, DbEnv*) + template <class InputIterator> + db_map(Db *dbp, DbEnv* envp, InputIterator first, + InputIterator last) : db_container(dbp, envp) + { + const char *errmsg; + + this->open_db_handles(dbp, envp, DB_BTREE, + DB_CREATE | DB_THREAD, 0); + if ((errmsg = verify_config(dbp, envp)) != NULL) { + THROW(InvalidArgumentException, ("Db*", errmsg)); + } + this->set_db_handle_int(dbp, envp); + + this->begin_txn(); + try { + insert(first, last); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + } + + // Copy constructor. The object is initialized to have the same + // contents as the x map object, do not copy properties because + // if we copy things like pdb, we are storing to the same db, so we + // create a new database, use it as the backing db, and store data + // into it. + /// Copy constructor. + /// Create an database and insert all key/data pairs in x into this + /// container. x's data members are not copied. + /// This function supports auto-commit. + /// \param x The other container to initialize this container. + /// \sa db_container(const db_container&) + db_map(const db_map<kdt, ddt, value_type_sub, iterator>& x) : + db_container(x) + { + verify_db_handles(x); + this->set_db_handle_int(this->clone_db_config( + x.get_db_handle()), x.get_db_env_handle()); + assert(this->get_db_handle() != NULL); + + this->begin_txn(); + try { + copy_db((db_map<kdt, ddt, value_type_sub, iterator>&)x); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + } + + virtual ~db_map(){} + //@} + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin insert function definitions. + /// Container content assignment operator. + /// This function supports auto-commit. + /// \param x The other container whose key/data pairs will be inserted + /// into this container. Old content in this containers are discarded. + /// \sa http://www.cplusplus.com/reference/stl/map/operator=/ + inline const self& operator=(const self& x) + { + ASSIGNMENT_PREDCOND(x) + db_container::operator =(x); + verify_db_handles(x); + assert(this->get_db_handle() != NULL); + this->begin_txn(); + try { + copy_db((self &)x); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + return x; + } + + /// \name Insert Functions + /// They have similiar usage as their C++ STL equivalents. + /// Note that when secondary index is enabled, each + /// db_container can create a db_multimap secondary container, + /// but the insert function is not functional for secondary containers. + /// \sa http://www.cplusplus.com/reference/stl/map/insert/ + //@{ + // + // Insert functions. Note that stl requires if the entry with x.key + // already exists, insert should not overwrite that entry and the + // insert should fail; but bdb Dbc::cursor(DB_KEYLAST) will replace + // existing data with new one, so we will first find whether we + // have this data, if have, return false; + // + // Can not internally use begin/commit_txn to wrap this call because + // it returns an iterator, which is closed after commit_txn(), and + // reopening it is wrong in multithreaded access. + /// Insert a single key/data pair if the key is not in the container. + /// \param x The key/data pair to insert. + /// \return A pair P, if insert OK, i.e. the inserted key wasn't in the + /// container, P.first will be the iterator sitting on the inserted + /// key/data pair, and P.second is true; otherwise P.first is an + /// invalid iterator and P.second is false. + pair<iterator,bool> insert (const value_type& x ) + { + pair<iterator,bool> ib; + iterator witr; + + init_itr(witr); + open_itr(witr); + + if (witr.move_to(x.first) == 0) {// has it + ib.first = witr; + ib.second = false; + // Cursor movements are not logged, no need to + // use transaction here. + return ib; + } + + witr.itr_status_ = witr.pcsr_->insert(x.first, x.second, + DB_KEYLAST); + assert(witr.itr_status_ == 0); + witr.refresh(false); + ib.first = witr; + ib.second = true; + + return ib; + } + + /// Insert with hint position. We ignore the hint position because + /// Berkeley DB knows better where to insert. + /// \param position The hint position. + /// \param x The key/data pair to insert. + /// \return The iterator sitting on the inserted key/data pair, or an + /// invalid iterator if the key was already in the container. + inline iterator insert (iterator position, const value_type& x ) + { + pair<iterator,bool> ib = insert(x); + return ib.first; + } + + // Member function template overload. + /// Range insertion. Insert a range [first, last) of key/data pairs + /// into this container. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + void insert (const db_map_base_iterator<kdt, realddt, ddt>& first, + const db_map_base_iterator<kdt, realddt, ddt>& last) + { + db_map_base_iterator<kdt, realddt, ddt> ii; + iterator witr; + + init_itr(witr); + open_itr(witr); + + for (ii = first; ii != last; ++ii) + witr.pcsr_->insert(ii->first, ii->second, + DB_KEYLAST); + } + + /// Range insertion. Insert a range [first, last) of key/data pairs + /// into this container. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + template<typename InputIterator> + void insert (InputIterator first, InputIterator last) + { + InputIterator ii; + iterator witr; + + init_itr(witr); + open_itr(witr); + + for (ii = first; ii != last; ++ii) + witr.pcsr_->insert(ii->first, ii->second, + DB_KEYLAST); + } + //@} + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin functions that create iterators. + /// \name Iterator Functions + /// The parameters in begin functions of this group have identical + /// meaning to thoes in db_vector::begin, refer to those functions + /// for details. + /// \sa db_vector::begin() + //@{ + /// Begin a read-write or readonly iterator which sits on the first + /// key/data pair of the database. + /// \param rmw Same as that of + /// db_vector::begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \param bulkretrieval Same as that of + /// db_vector::begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \param directdb_get Same as that of + /// db_vector::begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \param readonly Same as that of + /// db_vector::begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \return The created iterator. + /// \sa db_vector::begin(ReadModifyWriteOption, bool, + /// BulkRetrievalOption, bool) + // + iterator begin(ReadModifyWriteOption rmw = + ReadModifyWriteOption::no_read_modify_write(), + bool readonly = false, BulkRetrievalOption bulkretrieval = + BulkRetrievalOption::no_bulk_retrieval(), + bool directdb_get = true) + { + bool b_rmw; + u_int32_t bulk_retrieval = 0; + + b_rmw = (rmw == ReadModifyWriteOption::read_modify_write()); + // Read only cursor don't need acquire write lock. + if (readonly && b_rmw) + b_rmw = false; + if (readonly && bulkretrieval == BulkRetrievalOption:: + BulkRetrieval) + bulk_retrieval = bulkretrieval.bulk_buf_size(); + + iterator itr(dynamic_cast<db_container*>(this), + bulk_retrieval, b_rmw, directdb_get, readonly); + + open_itr(itr, readonly); + itr.first(); + return itr; + } + + /// Begin a read-only iterator. + /// \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 db_vector::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. + /// \sa db_vector::end() + inline iterator end() + { + iterator itr; + + // end() is at an invalid position. We don't know what key it + // refers, so itr_status_ and inval_pos_type are the only + // data members to identify an iterator's position. + // + itr.itr_status_ = INVALID_ITERATOR_POSITION; + itr.inval_pos_type_ = iterator::IPT_AFTER_LAST; + itr.owner_ = (db_container*)this; + return itr; + } + + /// \brief Create an open boundary iterator. + /// \return Returns an invalid const iterator denoting the position + /// after the last valid element of the container. + /// \sa db_vector::end() const + inline const_iterator end() const + { + const_iterator itr; + + // end() is at an invalid position. We don't know what key it + // refers, so itr_status_ and inval_pos_type are the only + // data members to identify an iterator's position. + // + itr.itr_status_ = INVALID_ITERATOR_POSITION; + itr.inval_pos_type_ = iterator::IPT_AFTER_LAST; + itr.owner_ = (db_container*)this; + return itr; + } + + /// Begin a read-write or readonly reverse iterator which sits on the + /// first key/data pair of the database. + /// \param rmw Same as that of + /// db_vector::begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \param bulkretrieval Same as that of + /// db_vector::begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \param directdb_get Same as that of + /// db_vector::begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \param read_only Same as that of + /// db_vector::begin(ReadModifyWrite, bool, BulkRetrievalOption, bool); + /// \return The created iterator. + /// \sa db_vector::begin(ReadModifyWriteOption, bool, + /// BulkRetrievalOption, bool) + /// \sa db_vector::begin(ReadModifyWrite, bool, BulkRetrievalOption, + /// bool); + reverse_iterator rbegin(ReadModifyWriteOption rmw = + ReadModifyWriteOption::no_read_modify_write(), + bool read_only = false, BulkRetrievalOption bulkretrieval = + BulkRetrievalOption::no_bulk_retrieval(), + bool directdb_get = true) + { + u_int32_t bulk_retrieval = 0; + + iterator itr = end(); + itr.rmw_csr_ = (rmw == ( + ReadModifyWriteOption::read_modify_write())) && !read_only; + itr.directdb_get_ = directdb_get; + itr.read_only_ = read_only; + if (read_only && bulkretrieval == BulkRetrievalOption:: + BulkRetrieval) + bulk_retrieval = bulkretrieval.bulk_buf_size(); + itr.bulk_retrieval_ = bulk_retrieval; + reverse_iterator ritr(itr); + + return ritr; + } + + /// Begin a read-only reverse iterator. + /// \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 db_vector::begin(ReadModifyWrite, bool, BulkRetrievalOption, + /// bool); + const_reverse_iterator rbegin(BulkRetrievalOption bulkretrieval = + BulkRetrievalOption::no_bulk_retrieval(), + bool directdb_get = true) const + { + const_iterator itr = end(); + itr.bulk_retrieval_ = (bulkretrieval == + BulkRetrievalOption::BulkRetrieval ? + bulkretrieval.bulk_buf_size() : 0); + itr.directdb_get_ = directdb_get; + itr.read_only_ = true; + const_reverse_iterator ritr(itr); + + return ritr; + } + + /// \brief Create an open boundary iterator. + /// \return Returns an invalid iterator denoting the position + /// before the first valid element of the container. + /// \sa db_vector::rend() + inline reverse_iterator rend() + { + reverse_iterator ritr; + ritr.inval_pos_type_ = iterator::IPT_BEFORE_FIRST; + return ritr; + } + + /// \brief Create an open boundary iterator. + /// \return Returns an invalid const iterator denoting the position + /// before the first valid element of the container. + /// \sa db_vector::rend() const + inline const_reverse_iterator rend() const + { + const_reverse_iterator ritr; + ritr.inval_pos_type_ = iterator::IPT_BEFORE_FIRST; + return ritr; + } + //@} // iterator functions + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // + // Begin functions that return container metadata. + /// \name Metadata Functions + /// These functions return metadata about the container. + //@{ + /// Get container category. + /// Determines whether this container object is a std::map<> + /// equivalent(when returns false) or that of hash_map<> + /// class(when returns true). This method is not in stl, but it + /// may be called by users because some operations are not supported + /// by both type(map/hash_map) of containers, you need to call this + /// function to distinguish the two types. dbstl will not stop you + /// from calling the wrong methods of this class. + /// \return Returns true if this container is a hash container based + /// on a Berkeley DB hash database; returns false if it is based on a + /// Berkeley DB btree database. + // + inline bool is_hash() const + { + DBTYPE dbtype = DB_UNKNOWN; + int ret; + + assert(this->get_db_handle() != NULL); + ret = this->get_db_handle()->get_type(&dbtype); + assert(ret == 0); + return dbtype == DB_HASH; + } + + /// Only for std::hash_map, return number of hash bucket in use. + /// This function supports auto-commit. + /// \return The number of hash buckets of the database. + size_type bucket_count() const + { + DBTYPE dbtype; + u_int32_t flags; + void *sp; + size_type sz; + int ret; + DbTxn*txn; + + assert(this->get_db_handle() != NULL); + ret = this->get_db_handle()->get_type(&dbtype); + assert(ret == 0); + if (dbtype != DB_HASH) { + THROW(InvalidFunctionCall, ("db_map<>::bucket_count")); + + } + flags = DB_FAST_STAT; + + // Here we use current_txn(), so we will get a valid + // transaction handle if we are using explicit transactions; + // and NULL if we are using autocommit, in which case bdb + // internal auto commit will be enabled automatically. + // + txn = ResourceManager::instance()-> + current_txn(this->get_db_handle()->get_env()); + BDBOP(this->get_db_handle()->stat(txn, &sp, flags), ret); + + sz = (size_type)(((DB_HASH_STAT*)sp)->hash_buckets); + free(sp); + return sz; + } + + /// Get container size. + // Return size of the map, can control whether compute + // accurately(slower if db is huge) or not. + /// This function supports auto-commit. + /// \return Return the number of key/data pairs in the container. + /// \param accurate This function uses database's statistics to get + /// the number of key/data pairs. The statistics mechanism will either + /// scan the whole database to find the accurate number or use the + /// number of last accurate scanning, and thus much faster. If there + /// are millions of key/data pairs, the scanning can take some while, + /// so in that case you may want to set the "accurate" parameter to + /// false. + size_type size(bool accurate = true) const + { + u_int32_t flags; + void *sp; + DBTYPE dbtype; + size_t sz; + int ret; + DbTxn*txn; + + flags = accurate ? 0 : DB_FAST_STAT; + BDBOP(this->get_db_handle()->get_type(&dbtype), ret); + + // Here we use current_txn(), so we will get a valid + // transaction handle if we are using explicit transactions; + // and NULL if we are using autocommit, in which case bdb + // internal auto commit will be enabled automatically. + // + txn = ResourceManager::instance()-> + current_txn(this->get_db_handle()->get_env()); + BDBOP(this->get_db_handle()->stat(txn, &sp, flags), ret); + + assert((dbtype == DB_BTREE) || (dbtype == DB_HASH)); + // dbtype is BTREE OR HASH, no others. + sz = dbtype == DB_BTREE ? ((DB_BTREE_STAT*)sp)-> + bt_ndata : ((DB_HASH_STAT*)sp)->hash_ndata; + free(sp); + return sz; + } + + /// 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. + /// \sa db_vector::max_size() + inline size_type max_size() const + { + return SIZE_T_MAX; + } + + /// Returns whether this container is empty. + /// This function supports auto-commit. + /// \return True if empty, false otherwise. + bool empty() const + { + // If we fail to move to the first record, the db is + // supposed to be empty. + const_iterator witr; + bool ret; + + try { + this->begin_txn(); + init_itr(witr); + open_itr(witr, true); + ret = witr.first() != 0; + this->commit_txn(); + return ret; + } catch (...) { + this->abort_txn(); + throw; + } + } + //@} + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin element accessors. + // + // Don't use transaction wrapper(begin/commit_txn) here because + // even insert(the only logged operation) is only part of the + // whole expression---the use case is dmmap[key] = value; + // So insert and another put call should + // be atomic, so there must be an outside transaction. + // + // As stated in STL specification, this method can't have "const" + // modifier because it is likely to insert a new record. + /// Retrieve data element by key. + /// This function returns an reference to the underlying data element + /// of the specified key x. The returned object can be used to read or + /// write the data element of the key/data pair. + /// Do use a data_type_wrap of db_map or value_type::second_type(they + /// are the same) type of variable to hold the return value of this + /// function. + /// \param x The target key to get value from. + /// \return Data element reference. + // + data_type_wrap operator[] (const key_type& x) + { + iterator witr, *pitr; + int ret; + + init_itr(witr); + open_itr(witr, false); + + if (witr.move_to(x) != 0) { + ddt d;//default value + DbstlInitializeDefault<ddt> initdef(d); + // Insert (x, d) as place holder. + witr.pcsr_->insert(x, d, DB_KEYLAST); + // Should be OK this time. + ret = witr.move_to(x); + assert(ret == 0); + // Return the reference to the data item of x. + } + + //witr->curpair_.second._DB_STL_SetDelItr(); + pitr = new iterator(witr); + data_type_wrap ref(pitr->curpair_.second); + ref._DB_STL_SetDelItr(); + return ref; + } + + // Only returns a right-value, no left value for assignment, so + // directly return the value rather than the ElementRef/ElementHolder + // wrapper. Must use a const reference to this container to call this + // const function. + // + /// Retrieve data element by key. + /// This function returns the value of the underlying data element of + /// specified key x. You can only read the element, but unable to + /// update the element via the return value of this function. And you + /// need to use the container's const reference to call this method. + /// \param x The target key to get value from. + /// \return Data element, read only, can't be used to modify it. + const ddt operator[] (const key_type& x) const + { + iterator witr; + + init_itr(witr); + open_itr(witr); + + // x is supposed to be in this map. + if (witr.move_to(x) != 0) { + THROW0(NoSuchKeyException); + + } + return witr.curpair_.second._DB_STL_value(); + } + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin functions that erase elements from the container. + // + // Can not reopen external/outside iterator's cursor, pos must + // already be in a transactional context. + // There is identical function in db_multimap<> and db_multiset + // for this function, we MUST keep the code consistent on update! + // Go to db_multimap<>::erase(const key_type&) to see why. + /// \name Erase Functions + /// \sa http://www.cplusplus.com/reference/stl/map/erase/ + //@{ + /// Erase a key/data pair at specified position. + /// \param pos An valid iterator of this container to erase. + inline void erase (iterator pos) + { + if (pos == end()) + return; + pos.pcsr_->del(); + } + + /// Erase elements by key. + /// All key/data pairs with specified key x will be removed from + /// underlying database. + /// This function supports auto-commit. + /// \param x The key to remove from the container. + /// \return The number of key/data pairs removed. + // There is identical function in db_multimap<> and db_multiset + // for this function, we MUST keep the code consistent on update! + // Go to db_multimap<>::erase(const key_type&) to see why. + // + size_type erase (const key_type& x) + { + size_type cnt; + iterator itr; + + this->begin_txn(); + try { + pair<iterator, iterator> rg = equal_range(x); + for (itr = rg.first, cnt = 0; itr != rg.second; ++itr) { + cnt++; + itr.pcsr_->del(); + } + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + return cnt; + } + + // Can not be auto commit because first and last are already open. + // There is identical function in db_multimap<> and db_multiset + // for this function, we MUST keep the code consistent on update! + // Go to db_multimap<>::erase(const key_type&) to see why. + /// Range erase. Erase all key/data pairs within the valid range + /// [first, last). + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + inline void erase (iterator first, iterator last) + { + iterator i; + + for (i = first; i != last; ++i) + i.pcsr_->del(); + } + //@} + + /// Swap content with container mp. + /// This function supports auto-commit. + /// \param mp The container to swap content with. + /// \param b_truncate: See db_vector::swap() for details. + /// \sa http://www.cplusplus.com/reference/stl/map/swap/ + /// db_vector::clear() + void swap (db_map<kdt, ddt, value_type_sub>& mp, bool b_truncate = true) + { + Db *swapdb = NULL; + std::string dbfname(64, '\0'); + + verify_db_handles(mp); + this->begin_txn(); + try { + swapdb = this->clone_db_config(this->get_db_handle(), + dbfname); + db_map<kdt, ddt, value_type_sub> tmap(swapdb, + swapdb->get_env(), begin(), end()); + clear(b_truncate);// Clear this db_map<> object. + typename db_map<kdt, ddt, value_type_sub>:: + iterator itr1, itr2; + itr1 = mp.begin(); + itr2 = mp.end(); + insert(itr1, itr2); + mp.clear(b_truncate); + itr1 = tmap.begin(); + itr2 = tmap.end(); + mp.insert(itr1, itr2); + tmap.clear(); + + swapdb->close(0); + if (dbfname[0] != '\0') { + swapdb = new Db(NULL, DB_CXX_NO_EXCEPTIONS); + swapdb->remove(dbfname.c_str(), NULL, 0); + swapdb->close(0); + delete swapdb; + } + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + } + + /// Clear contents in this container. + /// This function supports auto-commit. + /// \param b_truncate See db_vector::clear(bool) for details. + /// \sa db_vector::clear(bool) + 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 started. + if ((flag & DB_INIT_LOCK) || (flag & DB_INIT_CDB) || + (flag & DB_INIT_TXN)) + brmw = + ReadModifyWriteOption::read_modify_write(); + try { + // In if branch, truncate is capable of + // autocommit internally. + this->begin_txn(); + erase(begin(brmw, false), end()); + this->commit_txn(); + } catch (...) { + this->abort_txn(); + throw; + } + } + } + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin functions that searches a key in the map. + /// \name Searching Functions + /// The following functions are returning iterators, and they by + /// default return read-write iterators. If you intend to use the + /// returned iterator only to read, you should call the const version + /// of each function using a const reference to this container. + /// Using const iterators can potentially promote concurrency a lot. + /// You can also set the readonly parameter to each non-const version + /// of the functions to true if you don't use the returned iterator + /// to write, which also promotes concurrency and overall performance. + //@{ + /// Find the key/data pair with specified key x. + /// \param x The target key to find. + /// \return The valid const iterator sitting on the key x, or an + /// invalid one. + /// \sa http://www.cplusplus.com/reference/stl/map/find/ + const_iterator find (const key_type& x) const + { + const_iterator witr; + + init_itr(witr); + open_itr(witr, true); + if (witr.move_to(x)) + return ((self *)this)->end(); + + return witr; + } + + /// Find the greatest key less than or equal to x. + /// \param x The target key to find. + /// \return The valid const iterator sitting on the key, or an + /// invalid one. + /// \sa http://www.cplusplus.com/reference/stl/map/lower_bound/ + const_iterator lower_bound (const key_type& x) const + { + const_iterator witr; + + init_itr(witr); + open_itr(witr, true); + if (witr.move_to(x, DB_SET_RANGE)) + return ((self *)this)->end(); + + return witr; + } + + /// Find the range within which all keys equal to specified key x. + /// \param x The target key to find. + /// \return The range [first, last). + /// \sa http://www.cplusplus.com/reference/stl/map/equal_range/ + pair<const_iterator, const_iterator> + equal_range (const key_type& x) const + { + pair<const_iterator,const_iterator> pr; + const_iterator witr; + kdt k; + + init_itr(witr); + open_itr(witr, true); + if (witr.move_to(x, DB_SET_RANGE)) { + pr.first = ((self *)this)->end(); + pr.second = ((self *)this)->end(); + } else { + pr.first = witr; + // If no duplicate keys, move one next is sufficient. + if (witr.pcsr_->get_current_key(k) == 0 && k == x) + ++witr; + pr.second = witr; + } + + + return pr; + } + + /// Find the key/data pair with specified key x. + /// \param x The target key to find. + /// \param readonly Whether the returned iterator is readonly. + /// \return The valid iterator sitting on the key x, or an + /// invalid one. + /// \sa http://www.cplusplus.com/reference/stl/map/find/ + iterator find (const key_type& x, bool readonly = false) + { + iterator witr; + + init_itr(witr); + open_itr(witr, readonly); + if (witr.move_to(x)) + return ((self *)this)->end(); + + return witr; + } + + /// Find the greatest key less than or equal to x. + /// \param x The target key to find. + /// \param readonly Whether the returned iterator is readonly. + /// \return The valid iterator sitting on the key, or an + /// invalid one. + /// \sa http://www.cplusplus.com/reference/stl/map/lower_bound/ + iterator lower_bound (const key_type& x, bool readonly = false) + { + iterator witr; + + init_itr(witr); + open_itr(witr, readonly); + if (witr.move_to(x, DB_SET_RANGE)) + return ((self *)this)->end(); + + return witr; + } + + /// Find the range within which all keys equal to specified key x. + /// \param x The target key to find. + /// \param readonly Whether the returned iterator is readonly. + /// \return The range [first, last). + /// \sa http://www.cplusplus.com/reference/stl/map/equal_range/ + pair<iterator, iterator> + equal_range (const key_type& x, bool readonly = false) + { + pair<iterator,iterator> pr; + iterator witr; + kdt k; + + init_itr(witr); + open_itr(witr, readonly); + if (witr.move_to(x, DB_SET_RANGE)) { + pr.first = ((self *)this)->end(); + pr.second = ((self *)this)->end(); + } else { + pr.first = witr; + // If no dup, move one next is sufficient. + if (witr.pcsr_->get_current_key(k) == 0 && k == x) + ++witr; + pr.second = witr; + } + + + return pr; + } + + /// Count the number of key/data pairs having specified key x. + /// \param x The key to count. + /// \return The number of key/data pairs having x as key within the + /// container. + /// \sa http://www.cplusplus.com/reference/stl/map/count/ + size_type count (const key_type& x) const + { + int ret; + const_iterator witr; + try { + this->begin_txn(); + init_itr(witr); + open_itr(witr, true); + ret = witr.move_to(x); + this->commit_txn(); + if (ret != 0) + return 0;// No such key/data pair. + // No duplicates, so it must be one, we don't call + // Dbc::count because we don't have to. + // + else + return 1; + } catch (...) { + this->abort_txn(); + throw; + } + + } + + /// Find the least key greater than x. + /// \param x The target key to find. + /// \return The valid iterator sitting on the key, or an + /// invalid one. + /// \sa http://www.cplusplus.com/reference/stl/map/upper_bound/ + const_iterator upper_bound (const key_type& x) const + { + const_iterator witr; + + init_itr(witr); + open_itr(witr, true); + + if (witr.move_to(x, DB_SET_RANGE)) + return ((self *)this)->end(); + + kdt k; + + // x exists in db, and witr.pcsr_ points to x in db. + if (witr.pcsr_->get_current_key(k) == 0 && k == x) + ++witr;// No dup, so move one next is sufficient. + + return witr; + } + + /// Find the least key greater than x. + /// \param x The target key to find. + /// \param readonly Whether the returned iterator is readonly. + /// \return The valid iterator sitting on the key, or an + /// invalid one. + /// \sa http://www.cplusplus.com/reference/stl/map/upper_bound/ + iterator upper_bound (const key_type& x, bool readonly = false) + { + iterator witr; + + init_itr(witr); + open_itr(witr, readonly); + + if (witr.move_to(x, DB_SET_RANGE)) + return ((self *)this)->end(); + + kdt k; + + // x exists in db, and witr.pcsr_ points to x in db. + if (witr.pcsr_->get_current_key(k) == 0 && k == x) + ++witr;// No dup, so move one next is sufficient. + + return witr; + } + //@} + //////////////////////////////////////////////////////////////// + + // Compare function, return true if contents in m1 and m2 are + // identical otherwise return false. + // Note that we don't require the key-data pairs' order be identical + // Put into db_map<> rather than global to utilize transactional + // support. + /// Map content equality comparison operator. + /// This function does not rely on key order. For a set of keys S1 in + /// this container and another set of keys S2 of container m2, if + /// set S1 contains S2 and S2 contains S1 (S1 equals to S2) and each + /// data element of a key K in S1 from this container equals the data + /// element of K in m2, the two db_map<> containers equal. Otherwise + /// they are not equal. + /// \param m2 The other container to compare against. + /// \return Returns true if they have equal content, false otherwise. + bool operator==(const db_map<kdt, ddt, value_type_sub>& m2) const + { + bool ret; + const db_map<kdt, ddt, value_type_sub>& m1 = *this; + COMPARE_CHECK(m2) + verify_db_handles(m2); + try { + this->begin_txn(); + if (m1.size() != m2.size()) + ret = false; + else { + typename db_map<kdt, ddt, value_type_sub>:: + const_iterator i1, i2; + + for (i1 = m1.begin(); i1 != m1.end(); ++i1) { + if (m2.count(i1->first) == 0) { + ret = false; + goto exit; + } + i2 = m2.find(i1->first); + if ((i2->second == i1->second) == + false) { + ret = false; + goto exit; + } + } // for + + ret = true; + } +exit: + this->commit_txn(); + return ret; + + } catch (...) { + this->abort_txn(); + throw; + } + // Now that m1 and m2 has the same number of unique elements and all + // elements of m1 are in m2, thus there can be no element of m2 + // that dose not belong to m1, so we won't verify each element of + // m2 are in m1. + // + } + + /// Container unequality comparison operator. + /// \param m2 The container to compare against. + /// \return Returns false if equal, true otherwise. + bool operator!=(const db_map<kdt, ddt, value_type_sub>& m2) const + { + return !this->operator ==(m2); + } + + +protected: + + virtual const char* verify_config(Db*dbp, DbEnv* envp) const + { + DBTYPE dbtype; + u_int32_t oflags, sflags; + int ret; + const char *err = NULL; + + err = db_container::verify_config(dbp, envp); + if (err) + return err; + + BDBOP(dbp->get_type(&dbtype), ret); + BDBOP(dbp->get_open_flags(&oflags), ret); + BDBOP(dbp->get_flags(&sflags), ret); + + if (dbtype != DB_BTREE && dbtype != DB_HASH) + err = +"wrong database type, only DB_BTREE and DB_HASH allowed for db_map<> class"; + + if (oflags & DB_TRUNCATE) + err = +"do not specify DB_TRUNCATE flag to create a db_map<> object"; + if ((sflags & DB_DUP) || (sflags & DB_DUPSORT)) + err = +"db_map<> can not be backed by database permitting duplicate keys"; + if (sflags & DB_RECNUM) + err = "no DB_RECNUM flag allowed in db_map<>"; + + + return err; + + } + + + typedef ddt mapped_type; + typedef int (*db_compare_fcn_t)(Db *db, const Dbt *dbt1, + const Dbt *dbt2); + typedef u_int32_t (*h_hash_fcn_t) + (Db *, const void *bytes, u_int32_t length); + typedef db_set_iterator<kdt> db_multiset_iterator_t; + + static bool compare_keys(Db *pdb, const kdt& k1, const kdt& k2) + { + DBTYPE dbtype; + int ret; + bool bret; + u_int32_t sz1, sz2; + + assert(pdb != NULL); + ret = pdb->get_type(&dbtype); + assert(ret == 0); + db_compare_fcn_t comp = NULL; + + if (dbtype == DB_BTREE) + BDBOP(pdb->get_bt_compare(&comp), ret); + else // hash + BDBOP(pdb->get_h_compare(&comp), ret); + + DataItem key1(k1, true), key2(k2, true); + Dbt &kdbt1 = key1.get_dbt(); + Dbt &kdbt2 = key2.get_dbt(); + sz1 = kdbt1.get_size(); + sz2 = kdbt2.get_size(); + + if (comp == NULL) { + ret = memcmp(&k1, &k2, sz1 > sz2 ? sz2 : sz1); + return (ret == 0) ? (sz1 < sz2) : (ret < 0); + } + // Return strict weak ordering. + bret = (comp(pdb, &kdbt1, &kdbt2) < 0); + return bret; + } + + void open_itr(db_map_base_iterator<kdt, realddt, ddt>&itr, + bool readonly = false) const + { + u_int32_t oflags = 0; + int ret; + DbEnv *penv = this->get_db_handle()->get_env(); + + if (!readonly && penv != NULL) { + BDBOP((penv->get_open_flags(&oflags)) , ret); + if ((oflags & DB_INIT_CDB) != 0) + ((self *)this)->set_cursor_open_flags( + this->get_cursor_open_flags() | + DB_WRITECURSOR); + } + + itr.itr_status_ = itr.pcsr_->open((db_container*)this, + this->get_cursor_open_flags()); + itr.owner_ = (db_container*)this; + } + + 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(); + + if (!readonly && penv != NULL) { + BDBOP((penv->get_open_flags(&oflags)) , ret); + if ((oflags & DB_INIT_CDB) != 0) + ((self *)this)->set_cursor_open_flags( + this->get_cursor_open_flags() | + DB_WRITECURSOR); + } + itr.itr_status_ = itr.pcsr_->open((db_container*)this, + this->get_cursor_open_flags()); + itr.owner_ = (db_container*)this; + } + + inline void init_itr(db_map_base_iterator<kdt, realddt, ddt> & + witr) const { + typedef DbCursor<kdt, ddt> cursor_type; + witr.pcsr_.set_cursor(new cursor_type()); + witr.owner_ = (db_container*)this; + } + + // Do not use begin_txn/commit_txn in non-public(internal) methods, + // only wrap in public methods. + // + inline void copy_db(db_map<kdt, ddt, value_type_sub, iterator> &x) + { + + // Make sure clear can succeed if there are cursors + // open in other threads. + clear(false); + insert(x.begin(), x.end()); + + } + +};//db_map +//@} + + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +// +// db_multimap class template definition +// +// This class derives from db_map<>, using many of its methods, +// it also hides some functions that should not be used in +// this class, such as operator[]. +// +// The underlying db must allow duplicate. +// iterator_t is default argument, see forward declaration at the +// head of this file. +// iterator_t is default argument, see forward declaration at the +// head of this file +// +/// \ingroup dbstl_containers +//@{ +/// This class is the combination of std::multimap and hash_multimap. By +/// setting database handles as DB_BTREE or DB_HASH type respectively, you +/// will be using an equivalent of std::multimap or hash_multimap respectively. +/// Database(dbp) and environment(penv) handle requirement: +/// The dbp handle must meet the following requirement: +/// 1. Database type should be DB_BTREE or DB_HASH. +/// 2. Either DB_DUP or DB_DUPSORT flag must be set. Note that so far +/// Berkeley DB does not allow DB_DUPSORT be set and the database is storing +/// identical key/data pairs, i.e. we can't store two (1, 2), (1, 2) pairs +/// into a database D with DB_DUPSORT flag set, but only can do so with DB_DUP +/// flag set; But we can store a (1, 2) pair and a (1, 3) pair into D with +/// DB_DUPSORT flag set. So if your data set allows DB_DUPSORT flag, you +/// should set it to gain a lot of performance promotion. +/// 3. No DB_RECNUM flag set. +/// 4. No DB_TRUNCATE specified in database open flags. +/// 5. DB_THREAD must be set if you are sharing the database handle across +/// multiple threads directly, or indirectly by sharing the container object +/// across multiple threads. +/// \param kdt The key data type. +/// \param ddt The data data type. db_multimap stores key/data pairs. +/// \param value_type_sub Do not specify anything if ddt type is a +/// class/struct type; Otherwise, specify ElementHolder<ddt> to it. +/// \param iterator_t Never specify anything to this type parameter. It is +/// only used internally. +/// \sa db_container db_map +template<Typename kdt, Typename ddt, Typename value_type_sub, + Typename iterator_t> +class _exported db_multimap : public db_map<kdt, ddt, + value_type_sub, iterator_t> +{ +protected: + typedef db_multimap<kdt, ddt, value_type_sub, iterator_t> self; + typedef db_map<kdt, ddt, value_type_sub, iterator_t> base; +public: + typedef iterator_t iterator; + typedef typename iterator::const_version const_iterator; + typedef db_reverse_iterator<iterator, const_iterator> reverse_iterator; + typedef db_reverse_iterator<const_iterator, iterator> + const_reverse_iterator; + typedef kdt key_type; + typedef ddt data_type; + typedef value_type_sub data_type_wrap; + typedef pair<kdt, value_type_sub> value_type_wrap; + typedef pair<kdt, ddt> value_type; + typedef value_type_wrap* pointer; + typedef value_type_wrap& reference; + typedef const value_type& const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + //////////////////////////////////////////////////////////////// + // Begin constructors and destructor + /// Constructor. + /// See class detail for handle requirement. + /// \param dbp The database handle. + /// \param envp The database environment handle. + /// \sa db_map::db_map(Db*, DbEnv*) db_vector::db_vector(Db*, DbEnv*) + explicit db_multimap (Db *dbp = NULL, DbEnv* envp = NULL) : + base(*(new BulkRetrievalOption( + BulkRetrievalOption::BulkRetrieval))) + { + const char *errmsg; + + this->init_members(dbp, envp); + this->open_db_handles(dbp, envp, DB_BTREE, DB_CREATE | + DB_THREAD, DB_DUP); + // We can't call base(dbp, envp) here because it will verify + // failed and we can't call db_container directly, it is + // illegal to do so. + if ((errmsg = verify_config(dbp, envp)) != NULL) { + THROW(InvalidArgumentException, ("Db*", errmsg)); + + } + this->set_db_handle_int(dbp, envp); + this->set_auto_commit(dbp); + } + + /// Iteration constructor. + /// Iterates between first and last, setting + /// a copy of each of the sequence of elements as the content of + /// the container object. + /// This function supports auto-commit. + /// See class detail for handle requirement. + /// \param dbp The database handle. + /// \param envp The database environment handle. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + /// \sa db_map::db_map(Db*, DbEnv*, InputIterator, InputIterator) + /// db_vector::db_vector(Db*, DbEnv*) + // + template <class InputIterator> + db_multimap (Db *dbp, DbEnv* envp, InputIterator first, + InputIterator last) : base(*(new BulkRetrievalOption( + BulkRetrievalOption::BulkRetrieval))) + { + const char *errmsg; + + this->init_members(dbp, envp); + this->open_db_handles(dbp, envp, DB_BTREE, DB_CREATE | + DB_THREAD, DB_DUP); + // Note that we can't call base(dbp, envp) here because it + // will verify failed; And we can't call db_container + // directly because it is illegal to do so. + if ((errmsg = verify_config(dbp, envp)) != NULL) { + THROW(InvalidArgumentException, ("Db*", errmsg)); + + } + this->set_db_handle_int(dbp, envp); + this->set_auto_commit(dbp); + + + this->begin_txn(); + try { + insert(first, last); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + } + + /// Copy constructor. + /// Create an database and insert all key/data pairs in x into this + /// container. x's data members are not copied. + /// This function supports auto-commit. + /// \param x The other container to initialize this container. + /// \sa db_container(const db_container&) db_map(const db_map&) + db_multimap (const self& x) : base(*(new BulkRetrievalOption( + BulkRetrievalOption::BulkRetrieval))) + { + this->init_members(x); + verify_db_handles(x); + this->set_db_handle_int(this->clone_db_config( + x.get_db_handle()), x.get_db_env_handle()); + assert(this->get_db_handle() != NULL); + + this->begin_txn(); + try { + copy_db((self&)x); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + } + + virtual ~db_multimap(){} + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin functions that modify multimap content, e.g. insert, + // erase, assignment and swap. + // + /// Container content assignment operator. + /// This function supports auto-commit. + /// \param x The other container whose key/data pairs will be inserted + /// into this container. Old content in this containers are discarded. + /// \sa http://www.cplusplus.com/reference/stl/multimap/operator=/ + inline const self& operator=(const self&x) + { + ASSIGNMENT_PREDCOND(x) + db_container::operator =(x); + verify_db_handles(x); + assert(this->get_db_handle() != NULL); + this->begin_txn(); + try { + this->copy_db((self &)x); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + return x; + + } + + /// \name Insert Functions + /// \sa http://www.cplusplus.com/reference/stl/multimap/insert/ + //@{ + /// Range insertion. Insert a range [first, last) of key/data pairs + /// into this container. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + template<typename InputIterator> + void insert (InputIterator first, InputIterator last) + { + InputIterator ii; + iterator witr; + + init_itr(witr); + open_itr(witr); + + for (ii = first; ii != last; ++ii) + witr.pcsr_->insert(ii->first, ii->second, + DB_KEYLAST); + } + + // Compiler can't see the inherited version, unknown why. + /// Range insertion. Insert a range [first, last) of key/data pairs + /// into this container. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + inline void insert (const_iterator& first, const_iterator& last) { + base::insert(first, last); + } + + // Insert x into this container, the other two versions are + // inherited from db_map<> class. + // Methods returning an iterator or using an iterator as parameter + // can not be internally wrapped by + // begin/commit_txn because a cursor is inside its transaction, it + // must have been closed after transaction commit, and reopen is + // unsafe in multithreaded access. + // + /// Insert a single key/data pair if the key is not in the container. + /// \param x The key/data pair to insert. + /// \return A pair P, if insert OK, i.e. the inserted key wasn't in the + /// container, P.first will be the iterator sitting on the inserted + /// key/data pair, and P.second is true; otherwise P.first is an + /// invalid iterator and P.second is false. + inline iterator insert (const value_type& x) + { + iterator witr; + + this->init_itr(witr); + this->open_itr(witr); + witr.itr_status_ = witr.pcsr_->insert(x.first, x.second, + DB_KEYLAST); + witr.refresh(false); + return witr; + } + //@} + + /// Swap content with another multimap container. + /// This function supports auto-commit. + /// \param mp The other container to swap content with. + /// \param b_truncate See db_map::swap() for details. + /// \sa db_vector::clear() + void swap (db_multimap<kdt, ddt, value_type_sub>& mp, + bool b_truncate = true) + { + Db *swapdb = NULL; + std::string dbfname(64, '\0'); + + verify_db_handles(mp); + this->begin_txn(); + try { + swapdb = this->clone_db_config(this->get_db_handle(), + dbfname); + + db_multimap<kdt, ddt, value_type_sub> tmap( + swapdb, swapdb->get_env(), + this->begin(), this->end()); + // Clear this db_multimap<> object. + this->clear(b_truncate); + typename db_multimap<kdt, ddt, value_type_sub>:: + iterator mpbitr, mpeitr; + + mpbitr = mp.begin(); + mpeitr = mp.end(); + insert(mpbitr, mpeitr); + mp.clear(b_truncate); + mpbitr = tmap.begin(); + mpeitr = tmap.end(); + mp.insert(mpbitr, mpeitr); + tmap.clear(); + + swapdb->close(0); + if (dbfname[0] != '\0') { + swapdb = new Db(NULL, DB_CXX_NO_EXCEPTIONS); + swapdb->remove(dbfname.c_str(), NULL, 0); + swapdb->close(0); + delete swapdb; + } + this->commit_txn(); + } catch (...) { + this->abort_txn(); + throw; + } + + } + + // This method has identical code to db_map<>::erase(const key_type&), + // but we can NOT simply inherit and use + // that version because: + // 1. The db_map<>::erase called equal_range which is overloaded in + // db_multimap, so if we want the inherited erase to call the right + // version of equal_range, we have to make equal_range virtual + // 2. Making equal_range virtual will make the code not build--- The + // default template parameter can't be replaced by real parameter, + // unknow why. + // So we have to copy the code from db_map<> to here, and keep the + // code consistent on each update. + // Also, when I copy only this function, I found other erase overloaded + // functions also have to be copied from db_map<> to db_multimap and + // db_multiset, otherwise the code don't build, so I + // finally have to copy all versions of erase functions into db_multiset + // and db_multimap. When updating an erase function, do update all + // three versions. + /// \name Erase Functions + /// \sa http://www.cplusplus.com/reference/stl/multimap/erase/ + //@{ + /// Erase elements by key. + /// All key/data pairs with specified key x will be removed from + /// underlying database. + /// This function supports auto-commit. + /// \param x The key to remove from the container. + /// \return The number of key/data pairs removed. + size_type erase (const key_type& x) + { + size_type cnt; + iterator itr; + + this->begin_txn(); + try { + pair<iterator, iterator> rg = equal_range(x); + for (itr = rg.first, cnt = 0; itr != rg.second; ++itr) { + cnt++; + itr.pcsr_->del(); + } + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + return cnt; + } + + // Can not reopen external/outside iterator's cursor, pos must + // already be in a transactional context. + // There is identical function in db_multimap<> and db_multiset + // for this function, we MUST keep the code consistent on update! + // Go to db_multimap<>::erase(const key_type&) to see why. + // + /// Erase a key/data pair at specified position. + /// \param pos An valid iterator of this container to erase. + inline void erase (iterator pos) + { + if (pos == this->end()) + return; + pos.pcsr_->del(); + } + + // Can not be auto commit because first and last are already open. + // There is identical function in db_multimap<> and db_multiset + // for this function, we MUST keep the code consistent on update! + // Go to db_multimap<>::erase(const key_type&) to see why. + // + /// Range erase. Erase all key/data pairs within the valid range + /// [first, last). + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + inline void erase (iterator first, iterator last) + { + + for (iterator i = first; i != last; ++i) + i.pcsr_->del(); + } + //@} + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin functions that searches a key in the multimap. + /// \name Searching Functions + /// See of db_map's searching functions group for details about + /// iterator, function version and parameters. + /// \sa db_map + //@{ + /// Find the range within which all keys equal to specified key x. + /// \param x The target key to find. + /// \return The range [first, last). + /// \sa http://www.cplusplus.com/reference/stl/multimap/equal_range/ + pair<const_iterator, const_iterator> + equal_range (const key_type& x) const + { + pair<const_iterator,const_iterator> pr; + const_iterator witr; + kdt k; + + this->init_itr(witr); + this->open_itr(witr, true); + // Move witr to x if this contains x and return the itr, or if + // no x, position witr to the least key greater than x. + // + if (witr.move_to(x, DB_SET_RANGE)) { + pr.first = ((self *)this)->end(); + pr.second = ((self *)this)->end(); + } else { + pr.first = witr; + + // No dup, so move one next is sufficient. + if (witr.pcsr_->get_current_key(k) == 0 && k == x) + witr.next(DB_NEXT_NODUP); + pr.second = witr; + } + + return pr; + } + + /// Find the range within which all keys equal to specified key x. + /// \param x The target key to find. + /// \param readonly Whether the returned iterator is readonly. + /// \return The range [first, last). + /// \sa http://www.cplusplus.com/reference/stl/multimap/equal_range/ + pair<iterator,iterator> + equal_range (const key_type& x, bool readonly = false) + { + pair<iterator,iterator> pr; + iterator witr; + kdt k; + + this->init_itr(witr); + this->open_itr(witr, readonly); + // Move witr to x if this contains x and return the itr, or if + // no x, position witr to the least key greater than x. + // + if (witr.move_to(x, DB_SET_RANGE)) { + pr.first = ((self *)this)->end(); + pr.second = ((self *)this)->end(); + } else { + pr.first = witr; + + // No dup, so move one next is sufficient. + if (witr.pcsr_->get_current_key(k) == 0 && k == x) + witr.next(DB_NEXT_NODUP); + pr.second = witr; + } + + return pr; + } + + /// Find equal range and number of key/data pairs in the range. + /// This function also returns the number of elements within the + /// returned range via the out parameter nelem. + /// \param x The target key to find. + /// \param nelem The output parameter to take back the number of + /// key/data pair in the returned range. + /// \sa http://www.cplusplus.com/reference/stl/multimap/equal_range/ + pair<const_iterator, const_iterator> + equal_range_N (const key_type& x, size_t& nelem) const + { + int ret; + pair<const_iterator,const_iterator> pr; + size_t stepped; + const_iterator witr; + kdt k; + + this->init_itr(witr); + this->open_itr(witr, true); + // Move witr to x if this contains x and return the itr, or if + // no x, position witr to the least key greater than x. + // + if (witr.move_to(x, DB_SET_RANGE)) { + pr.first = ((self *)this)->end(); + pr.second = ((self *)this)->end(); + nelem = 0; + } else { + pr.first = witr; + if (witr.pcsr_->get_current_key(k) == 0 && k == x) { + for (stepped = 1, ret = + witr.pcsr_->next(DB_NEXT_DUP); ret == 0; + ret = witr.pcsr_->next(DB_NEXT_DUP), + stepped += 1) + ; + pr.second = ++witr; + nelem = stepped; + } else { + pr.second = witr; + nelem = 0; + } + } + return pr; + } + + /// Find equal range and number of key/data pairs in the range. + /// This function also returns the number of elements within the + /// returned range via the out parameter nelem. + /// \param x The target key to find. + /// \param nelem The output parameter to take back the number of + /// key/data pair in the returned range. + /// \param readonly Whether the returned iterator is readonly. + /// \sa http://www.cplusplus.com/reference/stl/multimap/equal_range/ + // + pair<iterator,iterator> + equal_range_N (const key_type& x, size_t& nelem, + bool readonly = false) + { + int ret; + pair<iterator,iterator> pr; + size_t stepped; + iterator witr; + kdt k; + + this->init_itr(witr); + this->open_itr(witr, readonly); + // Move witr to x if this contains x and return the itr, or if + // no x, position witr to the least key greater than x. + // + if (witr.move_to(x, DB_SET_RANGE)) { + pr.first = ((self *)this)->end(); + pr.second = ((self *)this)->end(); + nelem = 0; + } else { + pr.first = witr; + if (witr.pcsr_->get_current_key(k) == 0 && k == x) { + for (stepped = 1, ret = + witr.pcsr_->next(DB_NEXT_DUP); ret == 0; + ret = witr.pcsr_->next(DB_NEXT_DUP), + stepped += 1) + ; + pr.second = ++witr; + nelem = stepped; + } else { + pr.second = witr; + nelem = 0; + } + } + return pr; + } + + /// Count the number of key/data pairs having specified key x. + /// \param x The key to count. + /// \return The number of key/data pairs having x as key within the + /// container. + /// \sa http://www.cplusplus.com/reference/stl/multimap/count/ + size_type count (const key_type& x) const + { + int ret; + size_type cnt; + iterator witr; + + try { + this->begin_txn(); + this->init_itr(witr); + this->open_itr(witr, true); + ret = witr.move_to(x); + if (ret) + cnt = 0; + else + cnt = witr.pcsr_->count(); + this->commit_txn(); + } catch (...) { + this->abort_txn(); + throw; + } + + return cnt; + } + + /// Find the least key greater than x. + /// \param x The target key to find. + /// \return The valid iterator sitting on the key, or an + /// invalid one. + /// \sa http://www.cplusplus.com/reference/stl/multimap/upper_bound/ + const_iterator upper_bound ( + const key_type& x) const + { + int ret; + const_iterator witr; + + this->init_itr(witr); + this->open_itr(witr, true); + + // No key equal to or greater than x. + if (witr.move_to(x, DB_SET_RANGE)) + return ((self *)this)->end(); + + kdt k; + // x exists in db, and witr.pcsr_ points to x in db, + // need to move cursor to next different key. + // + if (witr.pcsr_->get_current_key(k) == 0 && k == x) + ret = witr.next(DB_NEXT_NODUP); + + return witr; + } + + /// Find the least key greater than x. + /// \param x The target key to find. + /// \param readonly Whether the returned iterator is readonly. + /// \return The valid iterator sitting on the key, or an + /// invalid one. + /// \sa http://www.cplusplus.com/reference/stl/multimap/upper_bound/ + iterator upper_bound (const key_type& x, bool readonly = false) + { + int ret; + iterator witr; + + this->init_itr(witr); + this->open_itr(witr, readonly); + + // No key equal to or greater than x. + if (witr.move_to(x, DB_SET_RANGE)) + return ((self *)this)->end(); + + kdt k; + // x exists in db, and witr.pcsr_ points to x in db, + // need to move cursor to next different key. + // + if (witr.pcsr_->get_current_key(k) == 0 && k == x) + ret = witr.next(DB_NEXT_NODUP); + + return witr; + } + //@} + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin functions that compare container content. + // + // In hash_multimap this function is a global compare function, + // return true if contents in m1 and m2 are identical + // otherwise return false. But we have multiple reasons to make + // it a member of db_multimap<>: + // 1. There need to be a temporary set to store values of a range, and + // db_multimap<> is quite likely to store huge amount of data, + // not suitable to store into std::set, let alone std::set is not + // completely multithread-safe, thus we store them into db_set<>, + // thus we need a temporary db handle, and call + // db_container::clone_db_handle() function to open the db handle. + // 2. We need the transactional support. Making this function + // autocommit is good to eliminate phantom issues. + // Note that we don't require the key-data pairs' order be identical, + // but we assume identical records of keys are adjacent, so that + // iteration will go through them all one by one; Also, the records' + // order of identical keys are unpridictable and irrelivent, so we + // should treat values of a equal range a set, and compare two value + // sets for equality when comparing a equal range of key X. + // + /** + Returns whether the two containers have identical content. + This function does not rely on key order. For a set of keys S1 in this + container and another set of keys S2 of container m2, if set S1 + contains S2 and S2 contains S1 (S1 equals to S2) and each set of data + elements of any key K in S1 from this container equals the set of data + elements of K in m2, the two db_multimap<> containers equal. Otherwise + they are not equal. Data element set comparison does not rely on order + either. + \param m2 The other container to compare against. + \return Returns true if they are equal, false otherwise. + */ + bool operator==(const db_multimap<kdt, ddt, value_type_sub>& m2) const + { + + typedef typename self::const_iterator mm_itr_t; + + COMPARE_CHECK(m2) + + bool ret = false, retset = false; + size_t n1, n2; + int ret2; + const self &m1 = *this; + DbTxn *ptxn = NULL; + DbEnv *penv; + Db *pdb; + const char *dbfilename, *dbname; + const char *pname1, *pname2; + string name1, name2; + u_int32_t oflags; + + verify_db_handles(m2); + pdb = this->get_db_handle(); + penv = pdb->get_env(); + try { + this->begin_txn(); + if (m1.size() != m2.size()) { + ret = false; + this->commit_txn(); + return ret; + } + BDBOP(pdb->get_dbname(&dbfilename, &dbname), ret2); + if (dbfilename == NULL) + pname1 = pname2 = NULL; + else { + + this->construct_db_file_name(name1); + this->construct_db_file_name(name2); + // Make name2 different from name1. + name2.push_back('2'); + pname1 = name1.c_str(); + pname2 = name2.c_str(); + } + + Db *value_set_db = open_db(penv, + pname1, DB_BTREE, DB_CREATE, 0); + + Db *value_set_db2 = open_db(penv, + pname2, DB_BTREE, DB_CREATE, 0); + + db_set<ddt, value_type_sub> s1(value_set_db, penv), + s2(value_set_db2, penv); + + mm_itr_t i1, i11; + pair<mm_itr_t, mm_itr_t> resrg1, resrg2; + for (i1 = m1.begin(); + i1 != m1.end(); + i1 = resrg1.second) { + + resrg1 = m1.equal_range_N(i1->first, n1); + resrg2 = m2.equal_range_N(i1->first, n2); + if (n1 != n2) { + ret = false; + retset = true; + break; + } + + if (n2 == 1 && !(resrg2.first->second == + resrg1.first->second)) { + ret = false; + retset = true; + break; + } + + for (i11 = resrg1.first; i11 != resrg1.second; + ++i11) + s1.insert(i11->second); + + for (i11 = resrg2.first; i11 != resrg2.second; + ++i11) + s2.insert(i11->second); + if (!(s1 == s2)) { + ret = false; + retset = true; + break; + } + s1.clear(); + s2.clear(); + + // Skip all equal keys in the range. + + + } // for + + if (!retset) // Care: there are breaks in the for loop. + ret = true; + + close_db(value_set_db); + close_db(value_set_db2); + + ptxn = this->current_txn(); + BDBOP(penv->get_open_flags(&oflags), ret2); + // The transaction handle in CDS is not a real + // transaction. + if (oflags & DB_INIT_CDB) + ptxn = NULL; + if (name1.length() > 0) + BDBOP2(penv->dbremove(ptxn, name1.c_str(), + NULL, 0), ret2, this->abort_txn()); + if (name2.length() > 0) + BDBOP2(penv->dbremove(ptxn, name2.c_str(), + NULL, 0), ret2, this->abort_txn()); + + this->commit_txn(); + return ret; + + } catch (...) { + this->abort_txn(); + throw; + } + // Now that m1 and m2 has the same number of unique elements and all + // elements of m1 are in m2, thus there can be no element of m2 that + // dose not belong to m1, so we won't verify each element of m2 are + // in m1. + // + + } // operator== + + /// Container unequality comparison operator. + /// \param m2 The container to compare against. + /// \return Returns false if equal, true otherwise. + bool operator!=(const db_multimap<kdt, ddt, value_type_sub>& m2) const + { + return !this->operator==(m2); + } + //////////////////////////////////////////////////////////////// + +protected: + typedef ddt mapped_type; + typedef value_type_sub tkpair; + typedef int (*bt_compare_fcn_t)(Db *db, const Dbt *dbt1, + const Dbt *dbt2); + + friend class db_map_iterator<kdt, _DB_STL_set_value<kdt>, + value_type_sub>; + friend class db_map_iterator<kdt, ddt, value_type_sub>; + + db_multimap(BulkRetrievalOption &opt) : base(opt){} +private: + value_type_sub operator[] (const key_type& x) + { + THROW(NotSupportedException, ("db_multimap<>::operator[]")); + } + + value_type_sub operator[] (const key_type& x) const + { + THROW(NotSupportedException, ("db_multimap<>::operator[]")); + + } + + virtual const char* verify_config(Db*dbp, DbEnv* envp) const + { + DBTYPE dbtype; + u_int32_t oflags, sflags; + int ret; + const char *err = NULL; + + err = db_container::verify_config(dbp, envp); + if (err) + return err; + + BDBOP(dbp->get_type(&dbtype), ret); + BDBOP(dbp->get_open_flags(&oflags), ret); + BDBOP(dbp->get_flags(&sflags), ret); + + if (dbtype != DB_BTREE && dbtype != DB_HASH) + err = +"wrong database type, only DB_BTREE and DB_HASH allowed for db_map<> class"; + if (oflags & DB_TRUNCATE) + err = +"do not specify DB_TRUNCATE flag to create a db_map<> object"; + + // Can't go without no dup or dupsort flag set. + if (!((sflags & DB_DUP) || (sflags & DB_DUPSORT))) + err = +"db_multimap<> can not be backed by database not permitting duplicate keys"; + + if (sflags & DB_RECNUM) + err = "no DB_RECNUM flag allowed in db_map<>"; + + return err; + + } + + inline void copy_db(db_multimap<kdt, ddt, value_type_sub> &x) + { + + // Make sure clear can succeed if there are cursors + // open in other threads. + this->clear(false); + insert(x.begin(), x.end()); + + } + +};// db_multimap<> +//@} //dbstl_containers +END_NS + +#endif // !_DB_STL_DB_MAP_H_ diff --git a/stl/dbstl_resource_manager.cpp b/stl/dbstl_resource_manager.cpp new file mode 100644 index 0000000..05d321e --- /dev/null +++ b/stl/dbstl_resource_manager.cpp @@ -0,0 +1,1039 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#include <assert.h> +#include <utility> + +#include "dbstl_resource_manager.h" +#include "dbstl_exception.h" +#include "dbstl_dbc.h" + +START_NS(dbstl) + +typedef struct { + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +} db_timespec; + +extern "C"{ +void __os_id (DB_ENV *, pid_t *, db_threadid_t*); +void __os_gettime(ENV *env, db_timespec *tp, int monotonic); +} + +using std::pair; +using std::make_pair; + +// Static data member definitions. +map<Db*, size_t> ResourceManager::open_dbs_; +map<DbEnv*, size_t> ResourceManager::open_envs_; +set<DbstlGlobalInnerObject *> ResourceManager::glob_objs_; +set<Db *> ResourceManager::deldbs; +set<DbEnv *> ResourceManager::delenvs; + +DbEnv * ResourceManager::mtx_env_ = NULL; +db_mutex_t ResourceManager::mtx_handle_ = 0; +db_mutex_t ResourceManager::mtx_globj_ = 0; + +#ifdef TLS_DEFN_MODIFIER +template <Typename T> TLS_DEFN_MODIFIER T *TlsWrapper<T>::tinst_ = NULL; +#elif defined(HAVE_PTHREAD_TLS) +static pthread_once_t once_control_ = PTHREAD_ONCE_INIT; +template <Typename T> +pthread_key_t TlsWrapper<T>::tls_key_; + +template<Typename T> +void tls_init_once(void) { + pthread_key_create(&TlsWrapper<T>::tls_key_, NULL); +} + +template<Typename T> +TlsWrapper<T>::TlsWrapper() +{ + pthread_once(&once_control_, tls_init_once<T>); +} +#else +#error "No suitable thread-local storage model configured" +#endif + +int ResourceManager::global_lock(db_mutex_t dbcontainer_mtx) +{ + int ret; + + ret = mtx_env_->mutex_lock(dbcontainer_mtx); + dbstl_assert(ret == 0); + return 0; +} + +int ResourceManager::global_unlock(db_mutex_t dbcontainer_mtx) +{ + int ret; + + ret = mtx_env_->mutex_unlock(dbcontainer_mtx); + dbstl_assert(ret == 0); + return 0; +} + +u_int32_t dbstl_strlen(const char *str) +{ + return (u_int32_t)strlen(str); +} + +void dbstl_strcpy(char *dest, const char *src, size_t num) +{ + strncpy(dest, src, num); +} + +int dbstl_strncmp(const char *s1, const char *s2, size_t num) +{ + return strncmp(s1, s2, num); +} + +int dbstl_wcsncmp(const wchar_t *s1, const wchar_t *s2, size_t num) +{ + return wcsncmp(s1, s2, num); +} + +int dbstl_strcmp(const char *s1, const char *s2) +{ + return strcmp(s1, s2); +} + +int dbstl_wcscmp(const wchar_t *s1, const wchar_t *s2) +{ + return wcscmp(s1, s2); +} + +u_int32_t dbstl_wcslen(const wchar_t *str) +{ + return (u_int32_t)wcslen(str); +} + +void dbstl_wcscpy(wchar_t *dest, const wchar_t *src, size_t num) +{ + wcsncpy(dest, src, num); +} + +// This function should be called in a single thread inside a process, before +// any use of dbstl. We don't want to rely on platform dependent mutex API, +// so we defer the synchronization to users. +void ResourceManager::global_startup() +{ + int ret; + db_timespec tnow; + + if (mtx_env_ == NULL) { + mtx_env_ = new DbEnv(DB_CXX_NO_EXCEPTIONS); + // Set cache size to 32k, to save space. + BDBOP(mtx_env_->set_cachesize(0, 32 * 1024, 1), ret); + BDBOP(mtx_env_->mutex_set_max(DBSTL_MAX_MTX_ENV_MUTEX), ret); + BDBOP2(mtx_env_->open(NULL, DB_PRIVATE | DB_CREATE, 0777), + ret, mtx_env_->close(0)); + BDBOP2(mtx_env_->mutex_alloc(DB_MUTEX_PROCESS_ONLY, + &mtx_handle_), ret, mtx_env_->mutex_free(mtx_handle_)); + BDBOP2(mtx_env_->mutex_alloc(DB_MUTEX_PROCESS_ONLY, + &mtx_globj_), ret, mtx_env_->mutex_free(mtx_globj_)); + __os_gettime(NULL, &tnow, 0); + srand((unsigned int)tnow.tv_sec); + } + +} + +ResourceManager::ResourceManager(void) +{ + + // Initialize process wide dbstl settings. If there are multiple + // threads, the global_startup should be called in a single thread + // before any use of dbstl. + global_startup(); +} + +void ResourceManager::close_db(Db *pdb) +{ + bool berase = false; + + if (pdb == NULL) + return; + db_csr_map_t::iterator itr = all_csrs_.find(pdb); + if (itr == all_csrs_.end()) + return; + + this->close_db_cursors(pdb); + + delete all_csrs_[pdb]; + all_csrs_.erase(itr); + pdb->close(0); + set<Db *>::iterator itrdb = deldbs.find(pdb); + // If new'ed by open_db, delete it. + if (itrdb != deldbs.end()) { + delete *itrdb; + berase = true; + } + + global_lock(mtx_handle_); + open_dbs_.erase(pdb); + if (berase) + deldbs.erase(itrdb); + + global_unlock(mtx_handle_); +} + +void ResourceManager::close_all_db_envs() +{ + u_int32_t oflags; + int ret; + size_t txnstk_sz; + + global_lock(mtx_handle_); + for (map<DbEnv*, size_t>::iterator i = open_envs_.begin(); + i != open_envs_.end(); ++i) { + BDBOP(i->first->get_open_flags(&oflags), ret); + txnstk_sz = env_txns_[i->first].size(); + if (oflags & DB_INIT_CDB) { + assert(txnstk_sz == 1); + BDBOP(env_txns_[i->first].top()->commit(0), ret); + } else + assert(txnstk_sz == 0); + + i->first->close(0); + } + + // Delete DbEnv objects new'ed by dbstl. + set<DbEnv *>::iterator itr2 = delenvs.begin(); + for (; itr2 != delenvs.end(); ++itr2) + delete *itr2; + + delenvs.clear(); + env_txns_.clear(); + open_envs_.clear(); + global_unlock(mtx_handle_); +} + +void ResourceManager::close_db_env(DbEnv *penv) +{ + u_int32_t oflags; + int ret; + size_t txnstk_sz; + bool berase = false; + + if (penv == NULL) + return; + map<DbEnv *, txnstk_t>::iterator itr = env_txns_.find(penv); + if (itr == env_txns_.end()) + return; + BDBOP(penv->get_open_flags(&oflags), ret); + txnstk_sz = itr->second.size(); + if (oflags & DB_INIT_CDB) { + assert(txnstk_sz == 1); + BDBOP(itr->second.top()->commit(0), ret); + } else + assert(txnstk_sz == 0); + env_txns_.erase(itr); + penv->close(0); + + set<DbEnv *>::iterator itrdb = delenvs.find(penv); + // If new'ed by open_db, delete it. + if (itrdb != delenvs.end()) { + delete penv; + berase = true; + } + + global_lock(mtx_handle_); + open_envs_.erase(penv); + if (berase) + delenvs.erase(itrdb); + global_unlock(mtx_handle_); +} + +void ResourceManager::close_all_dbs() +{ + map<Db *, size_t>::iterator itr; + set<Db *>::iterator itr2; + Db *pdb; + + global_lock(mtx_handle_); + for (itr = open_dbs_.begin(); itr != open_dbs_.end(); ++itr) { + pdb = itr->first; + this->close_db_cursors(pdb); + + delete all_csrs_[pdb]; + all_csrs_.erase(pdb); + pdb->close(0); + } + + // Delete Db objects new'ed by dbstl. + for (itr2 = deldbs.begin(); itr2 != deldbs.end(); ++itr2) + delete *itr2; + + deldbs.clear(); + open_dbs_.clear(); + + global_unlock(mtx_handle_); +} + +ResourceManager::~ResourceManager(void) +{ + u_int32_t oflags; + int ret; + + global_lock(mtx_handle_); + + for (map<Db*, size_t>::iterator i = open_dbs_.begin(); + i != open_dbs_.end(); ++i) { + this->close_db_cursors(i->first); + (i->second)--; + if (i->second == 0) { + + delete all_csrs_[i->first]; // Delete the cursor set. + all_csrs_.erase(i->first); + i->first->close(0); + + set<Db *>::iterator itrdb = deldbs.find(i->first); + // If new'ed by open_db, delete it. + if (itrdb != deldbs.end()) { + delete *itrdb; + deldbs.erase(itrdb); + } + } + } + + for (map<DbEnv*, size_t>::iterator i = open_envs_.begin(); + i != open_envs_.end(); ++i) { + BDBOP(i->first->get_open_flags(&oflags), ret); + if (oflags & DB_INIT_CDB) { + assert(env_txns_[i->first].size() == 1); + BDBOP(env_txns_[i->first].top()->commit(0), ret); + env_txns_[i->first].pop(); + } + + (i->second)--; + if (i->second == 0) { + assert(env_txns_[i->first].size() == 0); + i->first->close(0); + set<DbEnv *>::iterator itrdb = delenvs.find(i->first); + // If new'ed by open_db, delete it. + if (itrdb != delenvs.end()) { + delete *itrdb; + delenvs.erase(itrdb); + } + } + } + + global_unlock(mtx_handle_); + + for (db_csr_map_t::iterator itr3 = all_csrs_.begin(); + itr3 != all_csrs_.end(); ++itr3) + { + // Delete the cursor set. Above code may not have a chance to + // delete this set because the db(itr3->first) was already + // closed by another thread. + delete itr3->second; + } + // Don't bother to clear all_csrs_ since it is being destructed. + +// Don't handle transactions, leave them alone, because autocommit +// transactions must have been committed/aborted, and outside transactions +// should be handled by user code, and if they are not handled yet, +// the DbEnv::close will fail. +// Only handle the transaction for CDS mode---an DB_TXN* handle is opened +// at environment registration/creation by cdsgroup_begin, so we need to commit +// that transaction. +// +} + +Db* ResourceManager::open_db ( + DbEnv*penv, const char* filename, DBTYPE dbtype, + u_int32_t oflags, u_int32_t set_flags1, int mode, DbTxn* txn, + u_int32_t cflags, const char* dbname) +{ + int ret, ci = 0; + u_int32_t envf = 0, envoflags = 0; + DbTxn *ptxn = NULL; + Db *pdb = new Db(penv, cflags | DB_CXX_NO_EXCEPTIONS); + + if (penv) { + BDBOP(penv->get_open_flags(&envoflags), ret); + BDBOP(penv->get_flags(&envf), ret); + } + if (set_flags1) + BDBOP(pdb->set_flags(set_flags1), ret); + // If no transaction is specified and we really need one, begin a + // transaction and commit it before return, we don't commit + // passed-in transaction. + // + if (penv && ((envf & DB_AUTO_COMMIT) || + (envoflags & DB_INIT_TXN)) && txn == 0){ + ptxn = current_txn(penv); + BDBOP2(penv->txn_begin(ptxn, &txn, 0), ret, txn->abort()); + ci = 1; + } + if (txn == NULL) + BDBOP2(pdb->open(txn, filename, dbname, + dbtype, oflags, mode), + ret, (pdb->close(0))); + else + BDBOP2(pdb->open(txn, filename, dbname, + dbtype, oflags, mode), + ret, (pdb->close(0), txn->abort())); + if (ci && txn) + BDBOP(txn->commit(0), ret); + global_lock(mtx_handle_); + open_dbs_.insert(make_pair(pdb, 1u)); + pair<set<Db *>::iterator, bool> delinsret = deldbs.insert(pdb); + assert(delinsret.second); + global_unlock(mtx_handle_); + csrset_t *mycsrs = new csrset_t(); + all_csrs_.insert(make_pair(pdb, mycsrs)); + + return pdb; +} + +// Only called if the user does not supply an environment handle. +DbEnv* ResourceManager::open_env(const char* env_home, u_int32_t set_flags1, + u_int32_t oflags, u_int32_t cachesize, int mode, u_int32_t cflags) +{ + int ret; + + DbEnv *penv = new DbEnv(cflags | DB_CXX_NO_EXCEPTIONS); + if (set_flags1) + BDBOP(penv->set_flags(set_flags1, 1), ret); + BDBOP(penv->set_cachesize(0, cachesize, 1), ret); + BDBOP(penv->set_lk_max_lockers(2000), ret); + BDBOP(penv->set_lk_max_locks(2000), ret); + BDBOP(penv->set_lk_max_objects(2000), ret); + BDBOP2(penv->open(env_home, oflags, mode), ret, penv->close(0)); + + stack<DbTxn*> stk; + DbTxn *ptxn = NULL; + if (oflags & DB_INIT_CDB) { + BDBOP2(penv->cdsgroup_begin(&ptxn), ret, ptxn->commit(0)); + stk.push(ptxn); + } + + env_txns_.insert(make_pair(penv, stk)); + global_lock(mtx_handle_); + open_envs_.insert(make_pair(penv, 1u)); + delenvs.insert(penv); + global_unlock(mtx_handle_); + return penv; +} + +DbTxn* ResourceManager::current_txn(DbEnv*env) +{ + if (env_txns_.count(env) <= 0) + return NULL; + + stack<DbTxn*> &pstk = env_txns_[env]; + return pstk.size() != 0 ? pstk.top() : NULL; +} + +void ResourceManager::set_global_callbacks() +{ + 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); +} + +ResourceManager* ResourceManager::instance() +{ + ResourceManager *pinst = NULL; +#ifdef HAVE_PTHREAD_TLS + // Initialize the tls key. + pthread_once(&once_control_, tls_init_once<ResourceManager>); +#endif + + if ((pinst = TlsWrapper<ResourceManager>::get_tls_obj()) == NULL){ + TlsWrapper<ResourceManager>::set_tls_obj( + pinst = new ResourceManager()); + register_global_object(pinst); + set_global_callbacks(); + } + return pinst; +} + +int ResourceManager::open_cursor(DbCursorBase *dcbcsr, + Db *pdb, int flags) +{ + u_int32_t oflags = 0; + int ret; + + if (!pdb || !dcbcsr) + return 0; + + csrset_t::iterator csitr; + Dbc* csr = NULL; + dcbcsr->set_owner_db(pdb); + + DbTxn *ptxn = NULL; + DbTxn *ptxn2 = this->current_txn(pdb->get_env()); + if (ptxn2) { + ptxn = ptxn2; + dcbcsr->set_owner_txn(ptxn); + } + + if (pdb->get_env() != NULL){ + ret = pdb->get_env()->get_open_flags(&oflags); + dbstl_assert(ret == 0); + } + + // Call Dbc->cursor only if there is no active open cursor in the + // current thread, otherwise duplicate one from the existing cursor + // and use the locks already held in this thread. + // + csrset_t *pcsrset = NULL; + db_csr_map_t::iterator itrpcsrset = all_csrs_.find(pdb); + if (itrpcsrset == all_csrs_.end()) { // No such pair in current thread. + pcsrset = new csrset_t; + pair<db_csr_map_t::iterator, bool> insret0 = + all_csrs_.insert(make_pair(pdb, pcsrset)); + assert(insret0.second); + } else + pcsrset = itrpcsrset->second; + + assert(pcsrset != NULL); + if (pcsrset->size() == 0) { +newcursor: + BDBOP2(pdb->cursor(ptxn, &csr, flags), ret, + ((csr != NULL ? csr->close() : 1), + this->abort_txn(pdb->get_env()))); + } else { + // We have some open cursors, so try to dup from one. If we are + // in CDS mode, and trying to open a write cursor, we should + // duplicate from a write cursor. + csitr = pcsrset->begin(); + Dbc *csr22 = (*csitr)->get_cursor(); + assert(csr22 != NULL); + assert(!((oflags & DB_INIT_TXN) && (flags & DB_WRITECURSOR))); + // If opening a CDS write cursor, must find a write cursor + // to duplicate from. + if (((flags & DB_WRITECURSOR) != 0)) { + for (;csitr != pcsrset->end(); ++csitr) { + csr22 = (*csitr)->get_cursor(); + if (((DBC*)csr22)->flags & DBC_WRITECURSOR) { + // No need to abortTxn on fail in CDS. + BDBOP2(csr22->dup(&csr, DB_POSITION), + ret, csr->close()); + goto done; + } + } + goto newcursor; // No write cursor, create a new one. + + } else if (((oflags & DB_INIT_TXN) == 0) || + pdb->get_transactional() == 0) { + // We are opening a DS or CDS read cursor, or + // opening a cursor in + // a transactional environment from a database not + // transactionally created. + BDBOP2(csr22->dup(&csr, DB_POSITION), ret, + (csr->close(), this->abort_txn(pdb->get_env()))); + goto done; + } else { + // We are opening a transactional cursor, duplicate + // from a transactional one. + // We don't remove (close) the non-transactional ones, + // they are in use. + // Hold the locks already held in this thread, + // so need DB_POSITION flag. + // + DbTxn *ptxn3 = NULL; + DbCursorBase *dcbcursor = NULL; + csrset_t::iterator itr3, itr4; + int got_rg = 0; + + // Opening a cursor in a transactional environment + // with no transaction specified. This should not + // happen in the first place. + if (ptxn == NULL) + THROW(InvalidArgumentException, ("DbTxn*", +"Opening a cursor in a transactional environment but no transaction \ +is started specified")); + // When we check that there must be a valid transaction + // handle ptxn when opening a cursor in a + // transactional environment, the following code + // to delete cursors with no transaction + // is not required and never reached, + // but we will leave it there. + for (;csitr != pcsrset->end();) { + dcbcursor = *csitr; + ptxn3 = dcbcursor->get_owner_txn(); + if (ptxn3 == NULL) { + BDBOP(dcbcursor->close(), ret); + if (!got_rg){ + got_rg++; + itr3 = csitr; + } + } else if (got_rg) { + got_rg = 0; + itr4 = csitr; + pcsrset->erase(itr3, itr4); + csitr = pcsrset->begin(); + continue; + } + + if (ptxn3 == ptxn) { + csr22 = dcbcursor->get_cursor(); + BDBOP2(csr22->dup(&csr, DB_POSITION), + ret, (csr->close(), this-> + abort_txn(pdb->get_env()))); + goto done; + } + ++csitr; + + } + if (got_rg) { + pcsrset->erase(itr3, pcsrset->end()); + got_rg = 0; + } + + goto newcursor; + + } // else oflags & DB_INIT_TXN + } // else pcsrset->size() +done: + // Insert into current thread's db-cursor map and txn_csrs_ map, + // for later duplication. + // + dcbcsr->set_cursor(csr); + this->add_cursor(pdb, dcbcsr); + return 0; +} + +void ResourceManager::add_cursor(Db* dbp, DbCursorBase* dcbcsr) +{ + if (!dbp || !dcbcsr) + return; + assert(dcbcsr->get_cursor() != NULL); + + (all_csrs_[dbp])->insert(dcbcsr); + // Register to txncsrs_, we suppose current transaction is the context + // of this operation. + // + this->add_txn_cursor(dcbcsr, dbp->get_env()); +} + +// Close dbp's all open cursors opened in current thread, do not close +// those of dbp opened in other threads, Db::truncate requires dbp's +// all cursors of all threads should be closed, and it is user's duty +// to make sure other threads all close dbp's cursor, because if we +// close them here, it is also an error---multi-threaded access to the same +// Dbc* cursor should be serialized, we can't serialize with user code +// anyway. +// +size_t ResourceManager::close_db_cursors(Db* dbp1) +{ + int ret; + Db* dbp; + DbTxn *ptxn, *ptxn2; + csrset_t *pcset_txn; + + if (dbp1 == NULL) + return 0; + + dbp = dbp1; + db_csr_map_t::iterator itr0; + csrset_t::iterator itr; + + itr0 = all_csrs_.find(dbp1); + if (itr0 == all_csrs_.end()) + return 0; + + csrset_t *pcset = itr0->second; + + pcset_txn = NULL; + ptxn2 = ptxn = NULL; + size_t txncsr_sz = txn_csrs_.size(); + + for (itr = pcset->begin(), ret = 0; itr != pcset->end(); + ++itr, ret++) { + + BDBOP((*itr)->close(), ret); + if (txncsr_sz > 0) { + if (pcset_txn == NULL || ptxn != + (ptxn2 = (*itr)->get_owner_txn())) { + ptxn = ptxn2 ? ptxn2 : (*itr)->get_owner_txn(); + if (ptxn != NULL) + pcset_txn = txn_csrs_[ptxn]; + } + if (pcset_txn) + pcset_txn->erase(*itr); + } + + } + + // Don't delete the pcset or itr0 because this dbp1 may be used + // by other containers in this thread. + pcset->clear(); + // We don't delete the DbCursorBase object, it is still + // referenced by others. + return ret; +} + +// Close the cursor of csr and remove the entry containing csr from +// txn_csrs_ and all_csrs_. +int ResourceManager::remove_cursor(DbCursorBase*csr, + bool remove_from_txncsrs) +{ + int ret; + + if (csr == NULL) + return 0; + BDBOP(csr->close(), ret); + + if (remove_from_txncsrs) { + DbTxn *ptxn = csr->get_owner_txn(); + if (ptxn != NULL) { + txncsr_t::iterator itr = txn_csrs_.find(ptxn); + if (itr != txn_csrs_.end()) + itr->second->erase(csr); + } + } + + Db *pdb = csr->get_owner_db(); + if (pdb != NULL) + all_csrs_[pdb]->erase(csr); + + return ret; +} + +/* + * Remove cursors opened in transaction txn's context, should be called before + * commiting/aborting a transaction. + * Note that here we should remove the cursor from all_csrs_ too, + * by calling remove_cursor() function. + */ +void ResourceManager::remove_txn_cursor(DbTxn* txn) +{ + int ret; + + if (!txn) + return; + + txncsr_t::iterator itr0; + csrset_t::iterator itr; + itr0 = txn_csrs_.find(txn); + if (itr0 == txn_csrs_.end()) + return; // No cursor opened in this txn. + + csrset_t *pcsrs = itr0->second; + DbCursorBase *csr; + + // Remove(close and remove from csr registry) cursors + // opened in the transaction txn's context. + for (itr = pcsrs->begin(); itr != pcsrs->end(); ++itr) { + // This cursor should be closed now and removed + // from csr registry. + csr = *itr; + BDBOP(csr->close(), ret); + all_csrs_[csr->get_owner_db()]->erase(csr); + } + + delete pcsrs; + // Erase csrs belonging to txn. + txn_csrs_.erase(itr0); +} + +// Begin a new transaction from the specified environment env. +// When outtxn is non-zero, it supports nested txn, +// so the new transaction is started as a child transaction of the +// current one, and we push it into env1's transaction stack; +// Otherwise, we are starting an internal transaction for autocommit, +// no new transaction will be started, but current transaction's reference +// count will be incremented. +DbTxn* ResourceManager::begin_txn(u_int32_t flags, DbEnv*env1, int outtxn) +{ + DbEnv *env = env1; + DbTxn *ptxn, *txn = NULL; + int ret; + + if (!env1) + return NULL; + + assert(env_txns_.count(env1) > 0); + + stack<DbTxn*>&stk = env_txns_[env1]; + + // Not an outside transaction, so if there is transaction in stack, + // use it and increment its reference count. + if (outtxn == 0) { + // We have a transaction in stack, increment its reference + // count. + if (stk.size() > 0) { + txn = stk.top(); + // The txn was created externally, now we internally + // use it, so the reference count is 2. + map<DbTxn *, size_t>::iterator itr12; + if ((itr12 = txn_count_.find(txn)) == txn_count_.end()) + txn_count_.insert(make_pair(txn, 2u)); + else + txn_count_[txn]++; + } else { + // Empty stack, create a transaction and set reference count to 1. + BDBOP(env->txn_begin(NULL, &txn, flags), ret); + stk.push(txn); + txn_count_[txn] = 1;// the first to use it + txn_csrs_.insert(make_pair(txn, new csrset_t())); + } + + } else { // Creating a transaction by user, used outside of dbstl. + ptxn = stk.size() > 0 ? stk.top() : NULL; + + BDBOP(env->txn_begin(ptxn, &txn, flags), ret); + + // txn now is the current txn + stk.push(txn); + txn_csrs_.insert(make_pair(txn, new csrset_t())); + } + + return txn; +} + +void ResourceManager::commit_txn(DbEnv *env, u_int32_t flags) +{ + int ret; + DbTxn *ptxn; + + if (!env) + return; + + assert(env_txns_.count(env) > 0); + stack<DbTxn*> &stk = env_txns_[env]; + ptxn = stk.top(); + assert(ptxn != NULL); + size_t txncnt = txn_count_[ptxn]; + + if (txncnt > 1) // used internally + txn_count_[ptxn]--; + else { + txn_count_.erase(ptxn); + this->remove_txn_cursor(ptxn); + stk.pop(); + BDBOP(ptxn->commit(flags), ret); + + } + +} + +void ResourceManager::commit_txn(DbEnv *env, DbTxn *txn, u_int32_t flags) +{ + DbTxn *ptxn = NULL; + int ret; + + if (env == NULL || txn == NULL) + return; + + stack<DbTxn*> &stk = env_txns_[env]; + while (stk.size() > 0 && (ptxn = stk.top()) != txn) { + stk.pop(); + txn_count_.erase(ptxn);// may be in the txn_count_ map + this->remove_txn_cursor(ptxn); + // Child txns could be committed by parent txn, but c++ API + // can't delete the new'ed child txns when committing the + // parent txn, so have to commit them explicitly. + ptxn->commit(flags); + } + if (stk.size() == 0) + THROW(InvalidArgumentException, ( + "No such transaction created by dbstl")); + else { + stk.pop(); + txn_count_.erase(txn);// may be in the txn_count_ map + this->remove_txn_cursor(txn); + if (ptxn){ + BDBOP(ptxn->commit(flags), ret); + } else // could never happen + THROW(InvalidArgumentException, ( + "No such transaction created by dbstl")); + + } +} + +void ResourceManager::abort_txn(DbEnv *env, DbTxn *txn) +{ + int ret; + DbTxn *ptxn = NULL; + u_int32_t oflags; + + if (env == NULL || txn == NULL) + return; + + BDBOP (env->get_open_flags(&oflags), ret); + stack<DbTxn*> &stk = env_txns_[env]; + while (stk.size() > 0 && (ptxn = stk.top()) != txn) { + txn_count_.erase(ptxn);// may be in the txn_count_ map + this->remove_txn_cursor(ptxn); + stk.pop(); + // Child txns could be aborted by parent txn, but c++ API + // can't delete the new'ed child txns when aborting the + // parent txn, so have to abort them explicitly. + ptxn->abort(); + } + if (stk.size() == 0) + THROW(InvalidArgumentException, ( + "No such transaction created by dbstl")); + else { + stk.pop(); + txn_count_.erase(txn);// may be in the txn_count_ map + this->remove_txn_cursor(txn); + if (ptxn){ + if ((oflags & DB_INIT_CDB) == 0) + BDBOP(ptxn->abort(), ret); + } else // could never happen + THROW(InvalidArgumentException, ( + "No such transaction created by dbstl")); + + } +} + +// Abort current txn, close/remove its cursors and reference count. +void ResourceManager::abort_txn(DbEnv*env) +{ + int ret; + DbTxn *ptxn; + u_int32_t oflags; + + if (!env) + return; + env_txns_t::iterator itr(env_txns_.find(env)); + if (itr == env_txns_.end()) + return; + + stack<DbTxn*> &stk = itr->second; + if (stk.size() == 0) + return; + ptxn = stk.top(); + if (ptxn == NULL) + return; + this->remove_txn_cursor(ptxn); + BDBOP (env->get_open_flags(&oflags), ret); + + // Transactions handles created via cdsgroup_begin can not be aborted + // because they are not really transactions, they just borrow the + // DB_TXN structure to store a locker id. + if ((oflags & DB_INIT_CDB) == 0) + BDBOP(ptxn->abort(), ret); + txn_count_.erase(ptxn); + stk.pop(); +} + +DbTxn* ResourceManager::set_current_txn_handle(DbEnv *env, DbTxn *newtxn) +{ + assert(env_txns_.count(env) > 0); + stack<DbTxn*> &stk = env_txns_[env]; + DbTxn *ptxn = stk.top(); + stk.pop(); + stk.push(newtxn); + return ptxn; +} + +void ResourceManager::add_txn_cursor(DbCursorBase *dcbcsr, DbEnv *env) +{ + if (!env || !dcbcsr) + return; + + DbTxn *ptxn = this->current_txn(env); + if (ptxn == NULL) + return; + + u_int32_t oflags; + int ret; + + BDBOP(env->get_open_flags(&oflags), ret); + if ((oflags & DB_INIT_TXN) == 0) + return; + + txncsr_t::iterator itr; + csrset_t *pset; + + itr = txn_csrs_.find(ptxn); + pair<txncsr_t::iterator, bool> insret; + + if (itr == txn_csrs_.end()) { + insret = txn_csrs_.insert(make_pair(ptxn, new csrset_t())); + assert(insret.second); + itr = insret.first; + } + pset = itr->second; + pset->insert(dcbcsr); +} + +void ResourceManager::register_db(Db*pdb1) +{ + if (!pdb1) + return; + global_lock(mtx_handle_); + if (open_dbs_.count(pdb1) == 0) + open_dbs_.insert(make_pair(pdb1, 1u)); + else + open_dbs_[pdb1]++; + global_unlock(mtx_handle_); + csrset_t *pcsrset = new csrset_t(); + pair<db_csr_map_t::iterator, bool> insret = all_csrs_.insert( + make_pair(pdb1, pcsrset)); + if (!insret.second) + delete pcsrset; + +} + +void ResourceManager::register_db_env(DbEnv*env1) +{ + u_int32_t oflags = 0; + DbTxn *ptxn = NULL; + int ret; + + if (!env1) + return; + + stack<DbTxn*> stk; + BDBOP(env1->get_open_flags(&oflags), ret); + + if (oflags & DB_INIT_CDB) { + env1->cdsgroup_begin(&ptxn); + stk.push(ptxn); + } + + env_txns_.insert(make_pair(env1, stk)); + + global_lock(mtx_handle_); + if (open_envs_.count(env1) == 0) + open_envs_.insert(make_pair(env1, 1u)); + else + open_envs_[env1]++; + global_unlock(mtx_handle_); +} + +// Delete registered DbstlGlobalInnerObject objects. +void ResourceManager::global_exit() +{ + set<DbstlGlobalInnerObject *>::iterator itr; + global_lock(mtx_globj_); + for (itr = glob_objs_.begin(); itr != glob_objs_.end(); ++itr) + delete *itr; + global_unlock(mtx_globj_); + + mtx_env_->mutex_free(mtx_globj_); + mtx_env_->mutex_free(mtx_handle_); + delete mtx_env_; +} + +void ResourceManager::register_global_object(DbstlGlobalInnerObject *gio) +{ + global_lock(mtx_globj_); + glob_objs_.insert(gio); + global_unlock(mtx_globj_); +} + +END_NS diff --git a/stl/dbstl_resource_manager.h b/stl/dbstl_resource_manager.h new file mode 100644 index 0000000..6bca540 --- /dev/null +++ b/stl/dbstl_resource_manager.h @@ -0,0 +1,355 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#ifndef _DB_STL_RESOURCE_MANAGER_H__ +#define _DB_STL_RESOURCE_MANAGER_H__ + +#include <map> +#include <vector> +#include <stack> +#include <set> + +#include "dbstl_common.h" +#include "dbstl_inner_utility.h" + +START_NS(dbstl) + +class DbCursorBase; +using std::map; +using std::multimap; +using std::set; +using std::stack; + +/////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// ResourceManager class definition +// +// This class manages all the Berkeley DB handles and their mapping +// relationship. When it's only thread-specific instance is destructed, +// these handles are automatically closed in the correct order (the db and +// dbenv handles will be closed when the last thread using the handles in +// the process is destructed). +// +// The Db* and DbEnv* handles are process-wide global, they can be shared +// among multithreads, so they are stored into a static stl map, and access +// to the two map objects (open_dbs_ & open_envs_) is protected by a process +// wide mutex because few stl implementations support multithreaded access. +// We use reference counting in the two map objects to make sure each handle +// is closed when the last thread using it exits. Each thread sharing the +// handle should call ResourceManager::register_db/dbenv to tell DBSTL that +// it will use the handle, otherwise the handle may be closed prematurely. +// +// The transaction and cursor handles are thread specific. They are stored +// into stl containers and each instance of the ResourceManager is stored +// into thread local storage(TLS) of each thread. Thus the DB STL containers +// are thread safe. +// + +// This map contains cursors of all open databases opened in the same thread. +// We can only duplicate a cursor of the same database; We don't allow sharing +// Berkeley DB cursor handles across multiple threads, each thread needs to +// open their own cursor handle; +// +typedef map<Db *, set<dbstl::DbCursorBase *> *> db_csr_map_t; + +// The set of cursors that belong to a db handle or a transaction. +typedef set<dbstl::DbCursorBase *> csrset_t; +// The cursors opened in each transaction. +typedef map<DbTxn *, set<DbCursorBase *> *> txncsr_t; + +// This stack contains the transactions started in current thread. Each +// transaction is the child transaction of the one under it in the stack. +// +// We support nested transactions for those created by the dbstl +// users, but still keep reference counting for dbstl internally +// created transactions so that the autocommit methods are really +// autocommit with least overhead (nested transactions are overheads). The +// way we keep both nested transaction and reference counting to internal +// transactions in the same stack is: +// 1. When creating an external transaction, look at the stack top, if there +// is a transaction, it must be an external one too, so use it as the parent +// transaction to create the external transaction. +// 2. When creating an internal transaction, look at the stack top, if there +// is one, call it T, look for its the reference counting, if there is a +// reference count for it, T is an internal one, so we increment its +// reference count; Otherwise, T is an external one, and according to the DB +// autocommit semantics, this function should be in T's context, so we +// simply use T and add it to the reference counting structure, and set its +// reference count to 2. +// +// We don't support expanding a transaction across multiple threads, +// because there are many restrictions to doing so, making it meaningless. +// +typedef stack<DbTxn *> txnstk_t; +typedef map<DbEnv *, txnstk_t> env_txns_t; + +#ifdef WIN32 +#pragma warning( push ) +#pragma warning( disable:4251 ) +#endif +// This class is used to wrap a ResourceManager instance pointer, so that +// each thread has its own ResourceManager singleton. + +#ifdef TLS_DECL_MODIFIER +template <Typename T> +class TlsWrapper +{ +public: + static T *get_tls_obj() + { + return tinst_; + } + + static void set_tls_obj(T *objp) + { + tinst_ = objp; + } + +private: + TlsWrapper(){} + + // Thread local pointer to the instance of type T. + static TLS_DECL_MODIFIER T *tinst_; +}; // TlsWrapper<> + +#elif defined(HAVE_PTHREAD_TLS) +template <Typename T> +class TlsWrapper +{ +public: + static T *get_tls_obj() + { + return static_cast<T*>(pthread_getspecific(tls_key_)); + } + + static void set_tls_obj(T *objp) + { + pthread_setspecific(tls_key_, objp); + } + + // Friend declarations don't work portably, so we have to declare + // tls_key_ public. + static pthread_key_t tls_key_; +private: + TlsWrapper(); + +}; // TlsWrapper<> + +#else +#error "A multi-threaded build of STL for Berkeley DB requires thread local storage. None is configured." +#endif + +class _exported ResourceManager : public DbstlGlobalInnerObject +{ +private: + + ResourceManager(void); + // open_dbs_ & open_envs_ are shared among threads, protected by + // ghdl_mtx; + static map<Db *, size_t> open_dbs_; + static map<DbEnv *, size_t>open_envs_; + + // Transaction stack of all environments. Use a stack to allow nested + // transactions. The transaction at the top of the stack is the + // current active transaction. + // + env_txns_t env_txns_; + + // Cursors opened in a corresponding transaction context. When + // committing or aborting a transaction, first close all open cursors. + // + txncsr_t txn_csrs_; + + // If in container X, its methods X::A and X::B both call begin and + // commit transaction. X::A calls X::B after it's begin transaction + // call, then X::B will commit the transaction prematurally. To avoid + // will commit the only transaction prematurally, to avoid this, we use + // this, we use this map to record each transaction's reference count. + // Each begin/commit_txn() will increment/decrement the reference + // count, when reference count goes to 0, the transaction is committed. + // Abort_txn will unconditionally abort the transaction. + // + map<DbTxn *, size_t> txn_count_; + + // Contains the cursors opened in the current thread for each database, + // So that we can close them in the right way, freeing any Berkeley DB + // handles before exiting. + // + db_csr_map_t all_csrs_; + + // Remove cursors opened in the transaction txn's context, should be + // called before commiting or aborting a transaction. + // + void remove_txn_cursor(DbTxn *txn); + + // Add a cursor to the current transaction's set of open cursors. + void add_txn_cursor(DbCursorBase *dcbcsr, DbEnv *env); + + // The environment mtx_env_ and mtx_handle_ are used for synchronizing + // multiple threads' access to open_dbs_ and open_envs_ and glob_objs_. + // They are discarded when program exits, no deallocation/release + // is done. + static DbEnv *mtx_env_; + static db_mutex_t mtx_handle_; + static db_mutex_t mtx_globj_; + static set<DbstlGlobalInnerObject *> glob_objs_; + + // This set stores db handles that are new'ed by open_db, and thus + // should be deleted after this db is closed automatically by dbstl. + // If a db is new'ed and created by user without using open_db, users + // should delete it. + static set<Db *> deldbs; + static set<DbEnv *> delenvs; // Similar to deldbs, works with open_envs. + static void set_global_callbacks(); +public: + + // This function should be called in a single thread inside a process, + // before any use of dbstl. + static void global_startup(); + // Delete registered DbstlGlobalInnerObject objects. + static void global_exit(); + static void register_global_object(DbstlGlobalInnerObject *gio); + static DbEnv *get_mutex_env() { return mtx_env_; } + // Lock mtx_handle_, if it is 0, allocate it first. + static int global_lock(db_mutex_t dbcontainer_mtx); + static int global_unlock(db_mutex_t dbcontainer_mtx); + // Close pdb regardless of its reference count, users must make sure + // pdb is not used by others before calling this method. We can't + // close by reference count in this method, otherwise when the thread + // exits pdb's reference count is decremented twice. + void close_db(Db *pdb); + + // Close all db handles regardless of reference count, used to clean up + // the calling thread's ResourceManager singleton. + void close_all_dbs(); + + // Close specified db env handle and remove it from resource manager. + void close_db_env(DbEnv *penv); + + // Close and remove all db env registered in the resource manager. + // Used to clean up the calling thread's ResourceManager singleton. + void close_all_db_envs(); + + // Begin a new transaction in the specified environment. When outtxn + // is non-zero support nested transactions - the new transaction will + // be started as a child of the current transaction. If outtxn is 0 + // we are starting an internal transaction for autocommit, no new + // transaction will be started, but the current transaction's + // reference count will be incremented if it already has a reference + // count; otherwise, it was created by user, and we simply use this + // transaction, set its reference count to 2. + // + // This function is called by both containers to begin an internal + // transaction for autocommit, and by db stl users to begin an + // external transaction. + // + DbTxn *begin_txn(u_int32_t flags/* flags for DbEnv::txn_begin */, + DbEnv *env, int outtxn); + + // Decrement reference count if it exists and if it goes to 0, commit + // current transaction T opened in env; + // If T has no reference count(outside transaction), simply find + // it by popping the stack and commit it. + // + // This function is called by db_container to commit a transaction + // for auto commit, also can be called by db stl user to commit + // an external explicit transaction. + // + void commit_txn(DbEnv *env, u_int32_t flags = 0); + + // Commit specified transaction txn: find it by popping the stack, + // discard all its child transactions and commit it. + // + // This function is called by dbstl user to commit an external + // explicit transaction. + // + void commit_txn(DbEnv *env, DbTxn *txn, u_int32_t flags = 0); + + // Abort current transaction of the environment. + // + void abort_txn(DbEnv *env); + + // Abort specified transaction: find it by popping the stack, discard + // all its child transactions and abort it. + // + // This function is called by dbstl user to abort an external + // explicit transaction. + // + void abort_txn(DbEnv *env, DbTxn *txn); + + // Set env's current transaction handle. The original transaction + // handle is returned without aborting or commiting. This API can be + // used to share a transaction handle among multiple threads. + DbTxn* set_current_txn_handle(DbEnv *env, DbTxn *newtxn); + + // Register a Db handle. This handle and handles opened in it + // will be closed by ResourceManager, so application code must not + // try to close or delete it. Users can configure the handle before + // opening the Db and then register it via this function. + // + void register_db(Db *pdb1); + + // Register a DbEnv handle. This handle and handles opened in it + // will be closed by ResourceManager, so application code must not try + // to close or delete it. Users can configure the handle before + // opening the Environment and then register it via this function. + // + void register_db_env(DbEnv *env1); + + // Helper function to open a database and register it into + // ResourceManager. + // Users can set the create flags, open flags, db type, and flags + // needing to be set before open. + // + Db* open_db (DbEnv *penv, const char *filename, DBTYPE dbtype, + u_int32_t oflags, u_int32_t set_flags, int mode = 0644, + DbTxn* txn = NULL, u_int32_t cflags = 0, + const char *dbname = NULL); + + // Helper function to open a dbenv and register it into + // ResourceManager. + // Users can set the create flags, open flags, db type, and flags + // needing to be set before open. + // + DbEnv *open_env(const char *env_home, u_int32_t set_flags, + u_int32_t oflags = DB_CREATE | DB_INIT_MPOOL, + u_int32_t cachesize = 4 * 1024 * 1024, int mode = 0644, + u_int32_t cflags = 0/* Flags for DbEnv constructor. */); + + static ResourceManager *instance(); + + // Release all registered resource in the right order. + virtual ~ResourceManager(void); + + // Return current transaction of environment env, that is, the one on + // the transaction stack top, the active one. + // + DbTxn *current_txn(DbEnv *env); + + // Open a Berkeley DB cursor. + // + int open_cursor(DbCursorBase *dcbcsr, Db *pdb, int flags = 0); + + // Add a db-cursor mapping. + void add_cursor(Db *dbp, DbCursorBase *csr); + + // Close all cursors opened in the database. + size_t close_db_cursors(Db *dbp1); + + // Close and remove a cursor from ResourceManager. + // + int remove_cursor(DbCursorBase *csr, bool remove_from_txncsrs = true); + +}; // ResourceManager + +END_NS +#ifdef WIN32 +#pragma warning( pop ) +#endif +#endif // !_DB_STL_RESOURCE_MANAGER_H__ diff --git a/stl/dbstl_set.h b/stl/dbstl_set.h new file mode 100644 index 0000000..1aaf9f2 --- /dev/null +++ b/stl/dbstl_set.h @@ -0,0 +1,1583 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#ifndef _DB_STL_DB_SET_H_ +#define _DB_STL_DB_SET_H_ + + +#include "dbstl_common.h" +#include "dbstl_map.h" +#include "dbstl_dbc.h" +#include "dbstl_container.h" +#include "dbstl_resource_manager.h" +#include "dbstl_element_ref.h" +#include "dbstl_base_iterator.h" + +START_NS(dbstl) + +using std::pair; +using std::make_pair; + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// _DB_STL_set_value class template definition +// This class is used for db_set as value type because it inherits from +// db_map . It dose not need a byte for DB_HASH to work, hash db can store +// duplicated keys with empty data. +// +template <Typename T> +class _DB_STL_set_value +{ +public: + + inline _DB_STL_set_value(const ElementRef<T>&){ } + inline _DB_STL_set_value(const T&){} + inline _DB_STL_set_value(){} +}; + +/** \ingroup dbstl_iterators +@{ +\defgroup dbset_iterators Iterator classes for db_set and db_multiset. +db_set_base_iterator and db_set_iterator are the const iterator and iterator +class for db_set and db_multiset. They have identical behaviors to +std::set::const_iterator and std::set::iterator respectively. + +The difference between the two classes is that the db_set_base_iterator +can only be used to read its referenced value, while db_set_iterator allows +both read and write access. If the access pattern is readonly, it is strongly +recommended that you use the const iterator because it is faster and more +efficient. + +The two classes inherit several functions from db_map_base_iterator and +db_map_iterator respectively. +\sa db_map_base_iterator db_map_iterator + +@{ +*/ + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// db_set_base_iterator class template definition +// +// db_set_base_iterator class template is the const iterator class for db_set +// and db_multiset, it can only be used to read data. +// This class need to override operator* and operator-> because the +// db_set<>::value_type is different from db_map<>::value_type. We also has +// to copy the iterator movement operators into this class because the "self" +// type is different in db_map_base_iterator and this class. +// In db_set_base_iterator's inherited curpair_base_ pair, we store key to +// its first and second member, to work with db_map and db_multimap. +// Besides this, there is no further difference, so we can still safely make +// use of its inherited methods. +// +template <Typename kdt> +class _exported db_set_base_iterator : public + db_map_base_iterator<kdt, kdt, _DB_STL_set_value<kdt> > +{ +protected: + typedef db_set_base_iterator<kdt> self; + typedef db_map_base_iterator<kdt, kdt, _DB_STL_set_value<kdt> > base; + using db_base_iterator<kdt>::replace_current_key; +public: + + typedef kdt key_type; + typedef _DB_STL_set_value<kdt> ddt; + typedef kdt value_type; + typedef value_type& reference; + typedef value_type* pointer; + typedef value_type value_type_wrap; + typedef ptrdiff_t difference_type; + typedef difference_type distance_type; + // We have to use std iterator tags to match the parameter list of + // stl internal functions, so we don't need to write our own tag + // classes + // + typedef std::bidirectional_iterator_tag iterator_category; + + //////////////////////////////////////////////////////////////// + // Begin constructors and destructor definitions. + // + /// \name Constructors and destructor + /// Do not use these constructors to create iterators, but call + /// db_set::begin() const or db_multiset::begin() const to create + /// valid iterators. + //@{ + /// Destructor. + virtual ~db_set_base_iterator() + { + + } + + /// Constructor. + /// \param powner The container which creates this iterator. + /// \param b_bulk_retrieval The bulk read buffer size. 0 means + /// bulk read disabled. + /// \param brmw Whether set DB_RMW flag in underlying cursor. + /// \param directdbget Whether do direct database get rather than + /// using key/data values cached in the iterator whenever read. + /// \param b_read_only Whether open a read only cursor. Only effective + /// when using Berkeley DB Concurrent Data Store. + explicit db_set_base_iterator(db_container*powner, + u_int32_t b_bulk_retrieval = 0, bool brmw = false, + bool directdbget = true, bool b_read_only = false) + : base(powner, b_bulk_retrieval, brmw, directdbget, b_read_only) + { + this->is_set_ = true; + } + + /// Default constructor, dose not create the cursor for now. + db_set_base_iterator() : base() + { + this->is_set_ = true; + } + + /// Copy constructor. + /// \param s The other iterator of the same type to initialize this. + db_set_base_iterator(const db_set_base_iterator&s) : base(s) + { + this->is_set_ = true; + } + + /// Base copy constructor. + /// \param bo Initialize from a base class iterator. + db_set_base_iterator(const base& bo) : base(bo) + { + this->is_set_ = true; + } + //@} + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin functions that shift iterator positions. + /// \name Iterator movement operators. + /// These functions are identical to those of db_map_base_iterator + /// and db_map_iterator and db_set_iterator. Actually the iterator + /// movement functions in the four classes are the same. + //@{ + // But we have to copy them into the four classes because the + // return type, namely "self" is different in each class. + /// Post-increment. + /// \return This iterator after incremented. + /// \sa db_map_base_iterator::operator++() + inline self& operator++() + { + this->next(); + + return *this; + } + + /// Pre-increment. + /// \return Another iterator having the old value of this iterator. + /// \sa db_map_base_iterator::operator++(int) + inline self operator++(int) + { + self itr = *this; + + this->next(); + + return itr; + } + + /// Post-decrement. + /// \return This iterator after decremented. + /// \sa db_map_base_iterator::operator--() + inline self& operator--() + { + this->prev(); + + return *this; + } + + /// Pre-decrement. + /// \return Another iterator having the old value of this iterator. + /// \sa db_map_base_iterator::operator--(int) + self operator--(int) + { + self itr = *this; + this->prev(); + + return itr; + } + //@} + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin functions that retrieve values from iterator. + // + // This function returns a read only reference, you can only read + // its referenced data, not update it. + // curpair_base_ is always kept updated on iterator movement, but it + // is also updated here before making use of the value it points + // to, which is stored in curpair_base_ . + // Even if this iterator is invalid, this call is allowed, the + // default value of type T is returned. + // + /// \name Functions that retrieve values from iterator. + //@{ + /// Dereference operator. + /// Return the reference to the cached data element, which is an + /// object of type T. You can only use the return value to read + /// its referenced data element, can not update it. + /// \return Current data element reference object, i.e. ElementHolder + /// or ElementRef object. + inline reference operator*() + { + int ret; + + if (this->directdb_get_) { + ret = this->pcsr_->get_current_key( + this->curpair_base_.first); + dbstl_assert(ret == 0); + // curpair_base_.second is a _DB_STL_set_value<kdt> object, + // not used at all. Since const iterators can't be used to + // write, so this is not a problem. + } + // Returning reference, no copy construction. + return this->curpair_base_.first; + } + + // curpair_base_ is always kept updated on iterator movement, but it + // is also updated here before making use of the value it points + // to, which is stored in curpair_base_ . + // Even if this iterator is invalid, this call is allowed, the + // default value of type T is returned. + /// Arrow operator. + /// Return the pointer to the cached data element, which is an + /// object of type T. You can only use the return value to read + /// its referenced data element, can not update it. + /// \return Current data element reference object's address, i.e. + /// address of ElementHolder or ElementRef object. + inline pointer operator->() const + { + int ret; + + if (this->directdb_get_) { + ret = this->pcsr_->get_current_key( + this->curpair_base_.first); + dbstl_assert(ret == 0); + } + + return &(this->curpair_base_.first); + } + //@} + //////////////////////////////////////////////////////////////// + + /// \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) const + { + + if (from_db && !this->directdb_get_) + this->pcsr_->update_current_key_data_from_db( + DbCursorBase::SKIP_NONE); + this->pcsr_->get_current_key(this->curpair_base_.first); + this->curpair_base_.second = this->curpair_base_.first; + return 0; + } + +protected: + + // Declare friend classes to access protected/private members, + // while expose to outside only methods in stl specifications. + // + friend class db_set<kdt>; + + friend class db_map<kdt, _DB_STL_set_value<kdt>, + ElementHolder<kdt>, self>; + friend class db_map<kdt, _DB_STL_set_value<kdt>, + ElementRef<kdt>, self>; + + typedef pair<kdt, value_type> curpair_type; + +}; // db_set_base_iterator + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// db_set_iterator class template definition +// +// db_set_iterator class template is the iterator class for db_set and +// db_multiset, it can be used to update its referenced key/data pair. +// This class need to override operator* and operator-> because the +// db_set<>::value_type is different from db_map<>::value_type. besides +// this, there is no further difference, so we can still safely make +// use of db_set inherited methods. +// +template <Typename kdt, Typename value_type_sub> +class _exported db_set_iterator : + public db_map_iterator<kdt, _DB_STL_set_value<kdt>, value_type_sub> +{ +protected: + typedef db_set_iterator<kdt, value_type_sub> self; + typedef db_map_iterator<kdt, _DB_STL_set_value<kdt>, + value_type_sub> base; + +public: + + typedef kdt key_type; + typedef _DB_STL_set_value<kdt> ddt; + typedef kdt value_type; + typedef value_type_sub value_type_wrap; + typedef value_type_sub& reference; + typedef value_type_sub* pointer; + typedef ptrdiff_t difference_type; + typedef difference_type distance_type; + // We have to use std iterator tags to match the parameter list of + // stl internal functions, so we don't need to write our own tag + // classes + // + typedef std::bidirectional_iterator_tag iterator_category; + + //////////////////////////////////////////////////////////////// + // Begin constructors and destructor definitions. + /// \name Constructors and destructor + /// Do not use these constructors to create iterators, but call + /// db_set::begin() or db_multiset::begin() to create valid ones. + //@{ + /// Destructor. + virtual ~db_set_iterator() + { + + } + + /// Constructor. + /// \param powner The container which creates this iterator. + /// \param b_bulk_retrieval The bulk read buffer size. 0 means + /// bulk read disabled. + /// \param brmw Whether set DB_RMW flag in underlying cursor. + /// \param directdbget Whether do direct database get rather than + /// using key/data values cached in the iterator whenever read. + /// \param b_read_only Whether open a read only cursor. Only effective + /// when using Berkeley DB Concurrent Data Store. + explicit db_set_iterator(db_container*powner, + u_int32_t b_bulk_retrieval = 0, bool brmw = false, + bool directdbget = true, bool b_read_only = false) + : base(powner, b_bulk_retrieval, brmw, directdbget, b_read_only) + { + this->is_set_ = true; + } + + /// Default constructor, dose not create the cursor for now. + db_set_iterator() : base() + { + this->is_set_ = true; + } + + /// Copy constructor. + /// \param s The other iterator of the same type to initialize this. + db_set_iterator(const db_set_iterator&s) : base(s) + { + this->is_set_ = true; + } + + /// Base copy constructor. + /// \param bo Initialize from a base class iterator. + db_set_iterator(const base& bo) : base(bo) + { + this->is_set_ = true; + } + + /// Sibling copy constructor. + /// Note that this class does not derive from db_set_base_iterator + /// but from db_map_iterator. + /// \param bs Initialize from a base class iterator. + db_set_iterator(const db_set_base_iterator<kdt>&bs) : base(bs) + { + this->is_set_ = true; + } + //@} + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin functions that shift iterator positions. + /// \name Iterator movement operators. + //@{ + /// Pre-increment. + /// Identical to those of db_map_iterator. + /// \return This iterator after incremented. + /// \sa db_map_iterator::operator++() + inline self& operator++() + { + this->next(); + + return *this; + } + + /// Post-increment. + /// \return Another iterator having the old value of this iterator. + /// \sa db_map_iterator::operator++(int) + inline self operator++(int) + { + self itr = *this; + + this->next(); + + return itr; + } + + /// Pre-decrement. + /// \return This iterator after decremented. + /// \sa db_map_iterator::operator--() + inline self& operator--() + { + this->prev(); + + return *this; + } + + /// Post-decrement + /// \return Another iterator having the old value of this iterator. + /// \sa db_map_iterator::operator--(int) + self operator--(int) + { + self itr = *this; + this->prev(); + + return itr; + } + //@} + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin functions that retrieve values from iterator. + // + // This functions returns a read-write reference to its data element. + // Even if this iterator is invalid, this call is allowed, the + // default value of type T is returned. + /// \name Functions that retrieve values from iterator. + //@{ + /// 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. + /// \return Current data element reference object, i.e. ElementHolder + /// or ElementRef object. + inline reference operator*() + { + + if (this->directdb_get_) + refresh(true); + // returning reference, no copy construction + return this->curpair_.second; + } + + // curpair_ is always kept updated on iterator movement, but it + // is also updated here before making use of the value it points + // to, which is stored in curpair_ . + // Even if this iterator is invalid, this call is allowed, the + // default value of type T is returned. + /// 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. + /// \return Current data element reference object's address, i.e. + /// address of ElementHolder or ElementRef object. + inline pointer operator->() const + { + + if (this->directdb_get_) + refresh(true); + + return &(this->curpair_.second); + } + //@} + //////////////////////////////////////////////////////////////// + + /// \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) const + { + + if (from_db && !this->directdb_get_) + this->pcsr_->update_current_key_data_from_db( + DbCursorBase::SKIP_NONE); + this->pcsr_->get_current_key(this->curpair_.first); + this->curpair_.second._DB_STL_CopyData(this->curpair_.first); + this->set_curpair_base(this->curpair_.first, this->curpair_.first); + return 0; + } + +protected: + typedef pair<kdt, value_type_sub> curpair_type; + typedef db_set_base_iterator<kdt> const_version; + // Declare friend classes to access protected/private members, + // while expose to outside only methods in stl specifications. + // + friend class db_set<kdt, ElementHolder<kdt> >; + friend class db_set<kdt, ElementRef<kdt> >; + friend class db_multiset<kdt, ElementHolder<kdt> >; + friend class db_multiset<kdt, ElementRef<kdt> >; + friend class db_map<kdt, _DB_STL_set_value<kdt>, + ElementHolder<kdt>, self>; + friend class db_multimap<kdt, _DB_STL_set_value<kdt>, + ElementHolder<kdt>, self>; + friend class db_map<kdt, _DB_STL_set_value<kdt>, + ElementRef<kdt>, self>; + friend class db_multimap<kdt, _DB_STL_set_value<kdt>, + ElementRef<kdt>, self>; + + virtual void delete_me() const + { + delete this; + } + +}; // db_set_iterator +//@} // dbset_iterators +//@} // dbstl_iterators + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// db_set class template definition +// +// db_set<> inherits from db_map<>, storing nothing data, because +// we only need the key. It uses db_set_iterator<>/db_set_base_iterator +// as its iterator and const iterator respectively. +// The only difference between db_set<> and db_map<> classes +// is the value_type, so we redefine the insert function by directly copying +// the db_map<>::insert, in order to make use of the newly defined +// value_type of db_set. If typedef could be dynamically bound, we won't +// have to make this duplicated effort. +/// \ingroup dbstl_containers +/// This class is the combination of std::set and hash_set. By setting +/// database handles of DB_BTREE or DB_HASH type, you will be using the +/// equivalent of std::set or hash_set. This container stores the key in the +/// key element of a key/data pair in the underlying database, +/// but doesn't store anything in the data element. +/// Database and environment handle requirement: +/// The same as that of db_map. +/// \param kdt The key data type. +/// \param value_type_sub If kdt is a class/struct type, do not specify +/// anything in this parameter; Otherwise specify ElementHolder<kdt>. +/// \sa db_map db_container +// +template <Typename kdt, Typename value_type_sub> +class _exported db_set : public db_map<kdt, _DB_STL_set_value<kdt>, + value_type_sub, db_set_iterator<kdt, value_type_sub> > +{ +protected: + typedef db_set<kdt, value_type_sub> self; +public: + typedef db_set_iterator<kdt, value_type_sub> iterator; + typedef typename iterator::const_version const_iterator; + typedef db_reverse_iterator<iterator, const_iterator> reverse_iterator; + typedef db_reverse_iterator<const_iterator, iterator> + const_reverse_iterator; + typedef kdt key_type; + typedef ptrdiff_t difference_type; + // the ElementRef should store key value because key is also data, + // and *itr is key and data value + // + typedef kdt value_type; + typedef value_type_sub value_type_wrap; + typedef value_type_sub* pointer; + typedef value_type_sub& reference; + typedef value_type& const_reference; + typedef size_t size_type; + + //////////////////////////////////////////////////////////////// + // Begin constructors and destructor. + /// \name Constructors and destructor + //@{ + /// Create a std::set/hash_set equivalent associative container. + /// See the handle requirement in class details to pass correct + /// database/environment handles. + /// \param dbp The database handle. + /// \param envp The database environment handle. + /// \sa db_map(Db*, DbEnv*) db_container(Db*, DbEnv*) + explicit db_set (Db *dbp = NULL, DbEnv* envp = NULL) : base(dbp, envp){ + // There are some special handling in db_map_iterator<> + // if owner is set. + this->set_is_set(true); + } + + /// Iteration constructor. Iterates between first and last, + /// copying each of the elements in the range into + /// this container. + /// Create a std::set/hash_set equivalent associative container. + /// Insert a range of elements into the database. The range is + /// [first, last), which contains elements that can + /// be converted to type ddt automatically. + /// This function supports auto-commit. + /// See the handle requirement in class details to pass correct + /// database/environment handles. + /// \param dbp The database handle. + /// \param envp The database environment handle. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + /// \sa db_map(Db*, DbEnv*, InputIterator, InputIterator) + template <class InputIterator> + db_set (Db *dbp, DbEnv* envp, InputIterator first, + InputIterator last) : base(*(new BulkRetrievalOption( + BulkRetrievalOption::BulkRetrieval))) + { + + const char *errmsg; + + this->init_members(dbp, envp); + this->open_db_handles(dbp, envp, DB_BTREE, + DB_CREATE | DB_THREAD, 0); + if ((errmsg = this->verify_config(dbp, envp)) != NULL) { + THROW(InvalidArgumentException, ("Db*", errmsg)); + } + this->set_db_handle_int(dbp, envp); + this->set_auto_commit(dbp); + this->begin_txn(); + try { + insert(first, last); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + // There are some special handling in db_map_iterator<> + // if owner is set. + this->set_is_set(true); + + } + + /// Copy constructor. + /// Create a database and insert all key/data pairs in x into this + /// container. x's data members are not copied. + /// This function supports auto-commit. + /// \param x The source container to initialize this container. + /// \sa db_map(const db_map&) db_container(const db_container&) + db_set(const self& x) : base(*(new BulkRetrievalOption( + BulkRetrievalOption::BulkRetrieval))) + { + this->init_members(x); + verify_db_handles(x); + this->set_db_handle_int(this->clone_db_config( + x.get_db_handle()), x.get_db_env_handle()); + assert(this->get_db_handle() != NULL); + + this->begin_txn(); + try { + copy_db((self&)x); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + // There are some special handling in db_map_iterator<> + // if owner is set. + this->set_is_set(true); + } + + virtual ~db_set(){} + //@} + //////////////////////////////////////////////////////////////// + + /// Container content assignment operator. + /// This function supports auto-commit. + /// \param x The source container whose key/data pairs will be + /// inserted into the target container. Old content in the target + /// container is discarded. + /// \return The container x's reference. + /// \sa http://www.cplusplus.com/reference/stl/set/operator=/ + const self& operator=(const self& x) + { + ASSIGNMENT_PREDCOND(x) + db_container::operator =(x); + verify_db_handles(x); + assert(this->get_db_handle() != NULL); + this->begin_txn(); + try { + this->copy_db((self &)x); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + return x; + } + + //////////////////////////////////////////////////////////////// + // Begin key/value compare classes/functions. + // + // key_compare class definition, it is defined as an inner class, + // using underlying btree/hash db's compare function. It is the same + // as that of db_map<>, but because we have to redefine value_compare + // in this class, gcc forces us to also define key_compare again. + class key_compare + { + private: + Db*pdb; + public: + key_compare(Db*pdb1) + { + pdb = pdb1; + } + bool operator()(const kdt& k1, const kdt& k2) const + { + return compare_keys(pdb, k1, k2); + } + + }; // key_compare class definition + + class value_compare + { + key_compare kc; + public: + value_compare(Db*pdb) : kc(pdb) + { + + } + + bool operator()(const value_type& v1, + const value_type& v2) const + { + + return kc(v1, v2); + } + + }; // value_compare class definition + + /// Get value comparison functor. + /// \return The value comparison functor. + /// \sa http://www.cplusplus.com/reference/stl/set/value_comp/ + inline value_compare value_comp() const + { + return value_compare(this->get_db_handle()); + } + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin insert functions and swap function. + // + // Note that when secondary index is enabled, each db_container + // can create a db_multimap secondary container, but the insert + // function is not functional. + // + // Insert functions. Note that stl requires if the entry with x.key + // already exists, insert should not overwrite that entry and the + // insert should fail; but bdb Dbc::cursor(DB_KEYLAST) will replace + // existing data with new one, so we will first find whether we + // have this data, if have, return false; + /// \name Insert Functions + /// \sa http://www.cplusplus.com/reference/stl/set/insert/ + //@{ + /// Insert a single key/data pair if the key is not in the container. + /// \param x The key/data pair to insert. + /// \return A pair P, if insert OK, i.e. the inserted key wasn't in the + /// container, P.first will be the iterator positioned on the inserted + /// key/data pair, and P.second is true; otherwise P.first is an invalid + /// iterator equal to that returned by end() and P.second is false. + pair<iterator,bool> insert (const value_type& x ) + { + pair<iterator,bool> ib; + + _DB_STL_set_value<kdt> sv; + iterator witr; + + this->init_itr(witr); + this->open_itr(witr); + + if (witr.move_to(x) == 0) {// has it + ib.first = witr; + ib.second = false; + + return ib; + } + witr.itr_status_ = witr.pcsr_->insert(x, sv, DB_KEYLAST); + witr.refresh(false); + ib.first = witr; + ib.second = true; + + return ib; + } + + // NOT_AUTOCOMMIT_TAG + // Note that if first and last are on same db as this container, + // then insert may deadlock if there is no common transaction context + // for first and witr (when they are on the same page). + // The insert function in base class can not be directly used, + // got compile errors. + /// Range insertion. Insert a range [first, last) of key/data pairs + /// into this container. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + inline void insert (const_iterator& first, const_iterator& last) + { + const_iterator ii; + _DB_STL_set_value<kdt> v; + iterator witr; + + this->init_itr(witr); + + this->open_itr(witr); + + for (ii = first; ii != last; ++ii) + witr.pcsr_->insert(*ii, v, DB_KEYLAST); + } + + /// Range insertion. Insert a range [first, last) of key/data pairs + /// into this container. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + void insert (iterator& first, iterator& last) + { + iterator ii, witr; + _DB_STL_set_value<kdt> d; + + init_itr(witr); + open_itr(witr); + + for (ii = first; ii != last; ++ii) + witr.pcsr_->insert(*ii, d, DB_KEYLAST); + + } + + // Ignore the position parameter because bdb knows better + // where to insert. + /// Insert with hint position. We ignore the hint position because + /// Berkeley DB knows better where to insert. + /// \param position The hint position. + /// \param x The key/data pair to insert. + /// \return The iterator positioned on the inserted key/data pair, + /// or an invalid iterator if the key was already in the container. + inline iterator insert ( iterator position, const value_type& x ) + { + pair<iterator,bool> ib = insert(x); + return ib.first; + } + + /// Range insertion. Insert a range [first, last) of key/data pairs + /// into this container. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + template <class InputIterator> + void insert (InputIterator first, InputIterator last) + { + InputIterator ii; + _DB_STL_set_value<kdt> v; + iterator witr; + + this->init_itr(witr); + + this->open_itr(witr); + + for (ii = first; ii != last; ++ii) + witr.pcsr_->insert(*ii, v, DB_KEYLAST); + + } + //@} + + /// Swap content with another container. + /// This function supports auto-commit. + /// \param mp The container to swap content with. + /// \param b_truncate See db_vector::swap's b_truncate parameter + /// for details. + /// \sa db_map::swap() db_vector::clear() + void swap (db_set<kdt, value_type_sub>& mp, bool b_truncate = true) + { + Db *swapdb = NULL; + std::string dbfname(64, '\0'); + + verify_db_handles(mp); + this->begin_txn(); + try { + swapdb = this->clone_db_config(this->get_db_handle(), + dbfname); + + db_set<kdt, value_type_sub> tmap(swapdb, + swapdb->get_env(), this->begin(), this->end()); + typename db_set<kdt, value_type_sub>:: + iterator itr1, itr2; + + this->clear(b_truncate);// Clear this db_map<> object. + itr1 = mp.begin(); + itr2 = mp.end(); + this->insert(itr1, itr2); + mp.clear(b_truncate); + itr1 = tmap.begin(); + itr2 = tmap.end(); + mp.insert(itr1, itr2); + // tmap has no opened cursor, so simply truncate. + tmap.clear(); + + swapdb->close(0); + if (dbfname[0] != '\0') { + swapdb = new Db(NULL, DB_CXX_NO_EXCEPTIONS); + swapdb->remove(dbfname.c_str(), NULL, 0); + swapdb->close(0); + delete swapdb; + } + this->commit_txn(); + } catch (...) { + this->abort_txn(); + throw; + } + + } + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin container comparison functions. + // + // Return true if contents in m1 and m2 are identical otherwise + // return false. + // + // Note that we don't require the keys' order be identical, we are + // doing mathmatical set comparisons. + /// Set content equality comparison operator. + /// Return if the two containers have identical content. This function + /// does not rely on key order. + /// Two sets A and B are equal if and only if A contains B and B + /// contains A. + /// \param m2 The container to compare against. + /// \return Returns true if the two containers are equal, + /// false otherwise. + // + bool operator==(const db_set<kdt, value_type_sub>& m2) const + { + bool ret; + + COMPARE_CHECK(m2) + verify_db_handles(m2); + const db_set<kdt, value_type_sub>& m1 = *this; + + try { + this->begin_txn(); + if (m1.size() != m2.size()) + ret = false; + else { + typename db_set<kdt, value_type_sub>:: + const_iterator i1; + + for (i1 = m1.begin(); i1 != m1.end(); ++i1) { + if (m2.count(*i1) == 0) { + ret = false; + goto exit; + } + + } // for + ret = true; + } // else +exit: + this->commit_txn(); + // Now that m1 and m2 has the same number of unique elements and + // all elements of m1 are in m2, thus there can be no element of m2 + // that dose not belong to m1, so we won't verify each element of + // m2 are in m1. + return ret; + } catch (...) { + this->abort_txn(); + throw; + } + }// operator== + + /// Inequality comparison operator. + bool operator!=(const db_set<kdt, value_type_sub>& m2) const + { + return !this->operator==(m2); + } + //////////////////////////////////////////////////////////////// + +protected: + typedef int (*db_compare_fcn_t)(Db *db, const Dbt *dbt1, + const Dbt *dbt2); + + + typedef db_map<kdt, _DB_STL_set_value<kdt>, value_type_sub, + db_set_iterator<kdt, value_type_sub> > base; +private: + + value_type_sub operator[] (const key_type& x) + { + THROW(NotSupportedException, ("db_set<>::operator[]")); + + } + + value_type_sub operator[] (const key_type& x) const + { + THROW(NotSupportedException, ("db_set<>::operator[]")); + + } + + inline void copy_db(db_set<kdt, value_type_sub> &x) + { + + // Make sure clear can succeed if there are cursors + // open in other threads. + this->clear(false); + insert(x.begin(), x.end()); + + } + +}; // db_set<> + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// db_multiset class template definition +// +// db_multiset<> inherits from db_multimap, storing nothing as data, because +// we only need the key. It uses db_set_iterator<> and db_set_base_iterator<> +// as its iterator and const iterator respectively, so that +// we can safely make use of the inherited methods. The only difference +// is the value_type, so we redefine the insert functions by directly copying +// the db_map<>::insert versions, in order to make use of the newly defined +// value_type of db_multiset. If typedef could be dynamically bound, we won't +// have to make this duplicated effort. +/// \ingroup dbstl_containers +/// This class is the combination of std::multiset and hash_multiset. By +/// setting database handles of DB_BTREE or DB_HASH type respectively, you +/// will be using the equivalent of std::multiset or hash_multiset respectively. +/// This container stores the key in the key element of a key/data pair in +/// the underlying database, but doesn't store anything in the data element. +/// Database and environment handle requirement: +/// The requirement to these handles is the same as that to db_multimap. +/// \param kdt The key data type. +/// \param value_type_sub If kdt is a class/struct type, do not specify +/// anything in this parameter; Otherwise specify ElementHolder<kdt>. +/// \sa db_multimap db_map db_container db_set +// +template <Typename kdt, Typename value_type_sub> +class _exported db_multiset : public db_multimap<kdt, _DB_STL_set_value<kdt>, + value_type_sub, db_set_iterator<kdt, value_type_sub> > +{ +protected: + typedef db_multiset<kdt, value_type_sub> self; +public: + typedef db_set_iterator<kdt, value_type_sub> iterator; + typedef typename iterator::const_version const_iterator; + typedef db_reverse_iterator<iterator, const_iterator> reverse_iterator; + typedef db_reverse_iterator<const_iterator, iterator> + const_reverse_iterator; + typedef kdt key_type; + typedef ptrdiff_t difference_type; + // The ElementRef should store key value because key is also data, + // and *itr is key and data value. + // + typedef kdt value_type; + typedef value_type_sub value_type_wrap; + typedef value_type_sub& reference; + typedef const value_type& const_reference; + typedef value_type_sub* pointer; + typedef size_t size_type; + +public: + //////////////////////////////////////////////////////////////// + // Begin constructors and destructor. + /// \name Constructors and destructor + //@{ + /// Create a std::multiset/hash_multiset equivalent associative + /// container. + /// See the handle requirement in class details to pass correct + /// database/environment handles. + /// \param dbp The database handle. + /// \param envp The database environment handle. + /// \sa db_multimap(Db*, DbEnv*) + explicit db_multiset (Db *dbp = NULL, DbEnv* envp = NULL) : + base(dbp, envp) { + // There are special handling in db_map_iterator<> if owner + // is a set. + this->set_is_set(true); + } + + /// Iteration constructor. Iterates between first and last, + /// copying each of the elements in the range into + /// this container. + /// Create a std::multi/hash_multiset equivalent associative container. + /// Insert a range of elements into the database. The range is + /// [first, last), which contains elements that can + /// be converted to type ddt automatically. + /// This function supports auto-commit. + /// See the handle requirement in class details to pass correct + /// database/environment handles. + /// \param dbp The database handle. + /// \param envp The database environment handle. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + /// \sa db_multimap(Db*, DbEnv*, InputIterator, InputIterator) + template <class InputIterator> + db_multiset (Db *dbp, DbEnv* envp, InputIterator first, + InputIterator last) : base(*(new BulkRetrievalOption( + BulkRetrievalOption::BulkRetrieval))) + { + + const char *errmsg; + + this->init_members(dbp, envp); + this->open_db_handles(dbp, envp, DB_BTREE, DB_CREATE | + DB_THREAD, DB_DUP); + // Note that we can't call base(dbp, envp) here because it + // will verify failed; And we can't call db_container + // directly because it is illegal to do so. + if ((errmsg = verify_config(dbp, envp)) != NULL) { + THROW(InvalidArgumentException, ("Db*", errmsg)); + + } + this->set_db_handle_int(dbp, envp); + this->set_auto_commit(dbp); + + this->begin_txn(); + try { + insert(first, last); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + + // There are special handling in db_map_iterator<> if owner + // is a set. + this->set_is_set(true); + + } + + /// Copy constructor. + /// Create a database and insert all key/data pairs in x into this + /// container. x's data members are not copied. + /// This function supports auto-commit. + /// \param x The source container to initialize this container. + /// \sa db_multimap(const db_multimap&) db_container(const db_container&) + db_multiset(const self& x) : base(*(new BulkRetrievalOption( + BulkRetrievalOption::BulkRetrieval))) + { + this->init_members(x); + verify_db_handles(x); + this->set_db_handle_int(this->clone_db_config( + x.get_db_handle()), x.get_db_env_handle()); + assert(this->get_db_handle() != NULL); + + this->begin_txn(); + try { + copy_db((self&)x); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + + // There are special handling in db_map_iterator<> if owner + // is a set. + this->set_is_set(true); + } + + virtual ~db_multiset(){} + //@} + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin functions that modify the container's content, i.e. insert, + // erase, assignment and swap functions. + // + /// Container content assignment operator. + /// This function supports auto-commit. + /// \param x The source container whose key/data pairs will be + /// inserted into the target container. Old content in the target + /// container is discarded. + /// \return The container x's reference. + /// \sa http://www.cplusplus.com/reference/stl/multiset/operator=/ + inline const self& operator=(const self& x) + { + ASSIGNMENT_PREDCOND(x) + db_container::operator =(x); + verify_db_handles(x); + assert(this->get_db_handle() != NULL); + this->begin_txn(); + try { + this->copy_db((self &)x); + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + return x; + } + + // Note that when secondary index is enabled, each db_container + // can create a db_multimap secondary container, but the insert + // function is not functional. + /// \name Insert Functions + /// \sa http://www.cplusplus.com/reference/stl/multiset/insert/ + //@{ + /// Insert a single key if the key is not in the container. + /// \param x The key to insert. + /// \return An iterator positioned on the newly inserted key. If the + /// key x already exists, an invalid iterator equal to that returned + /// by end() function is returned. + inline iterator insert(const value_type& x ) + { + pair<iterator,bool> ib; + _DB_STL_set_value<kdt> sv; + iterator witr; + + this->init_itr(witr); + this->open_itr(witr); + + witr.itr_status_ = witr.pcsr_->insert(x, sv, DB_KEYLAST); + witr.refresh(false); + return witr; + } + + // Ignore the position parameter because bdb knows better + // where to insert. + /// Insert a single key with hint if the key is not in the container. + /// The hint position is ignored because Berkeley DB controls where + /// to insert the key. + /// \param x The key to insert. + /// \param position The hint insert position, ignored. + /// \return An iterator positioned on the newly inserted key. If the + /// key x already exists, an invalid iterator equal to that returned + /// by end() function is returned. + inline iterator insert ( iterator position, const value_type& x ) + { + return insert(x); + } + + /// Range insertion. Insert a range [first, last) of key/data pairs + /// into this container. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + template <class InputIterator> + void insert (InputIterator first, InputIterator last) + { + InputIterator ii; + _DB_STL_set_value<kdt> v; + + iterator witr; + + this->init_itr(witr); + this->open_itr(witr); + for (ii = first; ii != last; ++ii) + witr.pcsr_->insert(*ii, v, DB_KEYLAST); + + } + + // Member function template overload. + // This function is a specialization for the member template version + // dedicated to db_set<>. + // + /// Range insertion. Insert a range [first, last) of key/data pairs + /// into this container. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + void insert (db_set_iterator<kdt, value_type_sub>& first, + db_set_iterator<kdt, value_type_sub>& last) + { + db_set_iterator<kdt, value_type_sub> ii; + iterator witr; + _DB_STL_set_value<kdt> d; + + init_itr(witr); + open_itr(witr); + + for (ii = first; ii != last; ++ii) + witr.pcsr_->insert(*ii, d, DB_KEYLAST); + + + } + + // Member function template overload. + // This function is a specialization for the member template version + // dedicated to db_set<>. + // + /// Range insertion. Insert a range [first, last) of key/data pairs + /// into this container. + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + void insert (db_set_base_iterator<kdt>& first, + db_set_base_iterator<kdt>& last) + { + db_set_base_iterator<kdt> ii; + iterator witr; + _DB_STL_set_value<kdt> d; + + init_itr(witr); + open_itr(witr); + + for (ii = first; ii != last; ++ii) + witr.pcsr_->insert(*ii, d, DB_KEYLAST); + } + //@} + + // There is identical function in db_multimap<> and db_multiset + // for this function, we MUST keep the code consistent on update! + // Go to db_multimap<>::erase(const key_type&) to see why. + /// \name Erase Functions + /// \sa http://www.cplusplus.com/reference/stl/multiset/erase/ + //@{ + /// Erase elements by key. + /// All key/data pairs with specified key x will be removed from + /// the underlying database. + /// This function supports auto-commit. + /// \param x The key to remove from the container. + /// \return The number of key/data pairs removed. + size_type erase (const key_type& x) + { + size_type cnt; + iterator itr; + + this->begin_txn(); + try { + pair<iterator, iterator> rg = equal_range(x); + for (itr = rg.first, cnt = 0; + itr != rg.second; ++itr) { + cnt++; + itr.pcsr_->del(); + } + } catch (...) { + this->abort_txn(); + throw; + } + this->commit_txn(); + return cnt; + } + + // Can not reopen external/outside iterator's cursor, pos must + // already have been in a transactional context. + // There is identical function in db_multimap<> and db_multiset + // for this function, we MUST keep the code consistent on update! + // Go to db_multimap<>::erase(const key_type&) to see why. + // + /// Erase a key/data pair at specified position. + /// \param pos A valid iterator of this container to erase. + inline void erase (iterator pos) + { + if (pos == this->end()) + return; + pos.pcsr_->del(); + } + + // Can not be auto commit because first and last are already open. + // There is identical function in db_multimap<> and db_multiset + // for this function, we MUST keep the code consistent on update! + // Go to db_multimap<>::erase(const key_type&) to see why. + // + /// Range erase. Erase all key/data pairs within the valid range + /// [first, last). + /// \param first The closed boundary of the range. + /// \param last The open boundary of the range. + inline void erase (iterator first, iterator last) + { + iterator i; + + for (i = first; i != last; ++i) + i.pcsr_->del(); + } + //@} + + /// Swap content with another container. + /// This function supports auto-commit. + /// \param mp The container to swap content with. + /// \param b_truncate See db_multimap::swap() for details. + /// \sa db_map::swap() db_vector::clear() + void swap (db_multiset<kdt, value_type_sub>& mp, bool b_truncate = true) + { + Db *swapdb = NULL; + std::string dbfname(64, '\0'); + + verify_db_handles(mp); + this->begin_txn(); + try { + swapdb = this->clone_db_config(this->get_db_handle(), + dbfname); + + db_multiset<kdt, value_type_sub> tmap(swapdb, + swapdb->get_env(), this->begin(), this->end()); + this->clear(b_truncate);// Clear this db_map<> object. + typename db_multiset<kdt, value_type_sub>:: + iterator itr1, itr2; + itr1 = mp.begin(); + itr2 = mp.end(); + this->insert(itr1, itr2); + mp.clear(b_truncate); + itr1 = tmap.begin(); + itr2 = tmap.end(); + mp.insert(itr1, itr2); + + tmap.clear(); + + swapdb->close(0); + if (dbfname[0] != '\0') { + swapdb = new Db(NULL, DB_CXX_NO_EXCEPTIONS); + swapdb->remove(dbfname.c_str(), NULL, 0); + swapdb->close(0); + delete swapdb; + } + this->commit_txn(); + + } catch (...) { + swapdb->close(0); + this->abort_txn(); + throw; + } + + } + //////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////// + // Begin container comparison functions. + // + // Compare two db_multiset containers for equality, containers + // containing identical set of keys are considered equal, keys' + // order are not presumed or relied upon by this comparison. + /// Container content equality compare operator. + /// This function does not rely on key order. + /// Two sets A and B are equal if and only if + /// for each and every key K having n occurrences in A, K has n + /// occurrences in B, and for each and every key K` having N + /// occurrences in B, K` has n occurrences in A. + /// \param m2 The container to compare against. + /// \return Returns true if the two containers are equal, + /// false otherwise. + bool operator==(const self& m2) const + { + COMPARE_CHECK(m2) + verify_db_handles(m2); + + const db_multiset<kdt, value_type_sub> &m1 = *this; + const_iterator i1, i11; + pair<const_iterator, const_iterator> resrg1, resrg2; + size_t n1, n2; + bool ret = false; + + try { + this->begin_txn(); + if (m1.size() != m2.size()) + ret = false; + else { + for (i1 = m1.begin(); i1 != m1.end(); ) { + resrg1 = m1.equal_range_N(*i1, n1); + resrg2 = m2.equal_range_N(*i1, n2); + if (n1 != n2) { + ret = false; + goto exit; + } + + // If both is 1, resrg2 may contain no i1->first. + if (n2 == 1 && !(*(resrg2.first) == + *(resrg1.first))) { + ret = false; + goto exit; + } + // m1 and m2 contains identical set of i1->first key, + // so move on, skip all equal keys in the range. + // + i1 = resrg1.second; + + } // for + ret = true; + + }// else +exit: + this->commit_txn(); + return ret; + } catch (...) { + this->abort_txn(); + throw; + } + + // Now that m1 and m2 has the same number of unique elements and all + // elements of m1 are in m2, thus there can be no element of m2 that + // dose not belong to m1, so we won't verify each element of m2 are + // in m1. + + } // operator== + + /// Inequality comparison operator. + bool operator!=(const self& m2) const + { + return !this->operator==(m2); + } + //////////////////////////////////////////////////////////////// + +protected: + + typedef int (*db_compare_fcn_t)(Db *db, const Dbt *dbt1, + const Dbt *dbt2); + typedef db_multimap<kdt, _DB_STL_set_value<kdt>, + value_type_sub, db_set_iterator<kdt, value_type_sub> > base; + + // Declare base our friend, we can't use 'friend class base' to do + // this declaration on gcc. + friend class db_multimap<kdt, _DB_STL_set_value<kdt>, + value_type_sub, db_set_iterator<kdt, value_type_sub> >; + // Declare iterator our friend, we can't use 'friend class iterator' + // to do this declaration on gcc. + friend class db_set_iterator<kdt, value_type_sub>; + friend class db_map_iterator<kdt, _DB_STL_set_value<kdt>, + value_type_sub>; + friend class db_map_iterator<kdt, _DB_STL_set_value<kdt> >; + +private: + + inline void copy_db(db_multiset<kdt, value_type_sub> &x) + { + + // Make sure clear can succeed if there are cursors + // open in other threads. + this->clear(false); + insert(x.begin(), x.end()); + + } + + // Prevent user calling the inherited version. + value_type operator[] (const key_type& x) + { + THROW(NotSupportedException, ("db_multiset<>::operator[]")); + + } + + value_type operator[] (const key_type& x) const + { + THROW(NotSupportedException, ("db_multiset<>::operator[]")); + + } + + virtual const char* verify_config(Db*dbp, DbEnv* envp) const + { + DBTYPE dbtype; + u_int32_t oflags, sflags; + int ret; + const char *err = NULL; + + err = db_container::verify_config(dbp, envp); + if (err) + return err; + + BDBOP(dbp->get_type(&dbtype), ret); + BDBOP(dbp->get_open_flags(&oflags), ret); + BDBOP(dbp->get_flags(&sflags), ret); + + if (dbtype != DB_BTREE && dbtype != DB_HASH) + err = +"wrong database type, only DB_BTREE and DB_HASH allowed for db_map<> class"; + if (oflags & DB_TRUNCATE) + err = +"do not specify DB_TRUNCATE flag to create a db_map<> object"; + + // Must set DB_DUP and NOT DB_DUPSORT. + if (!(sflags & DB_DUP) || (sflags & DB_DUPSORT)) + err = +"db_multiset<> requires a database with DB_DUP set and without DB_DUPSORT set."; + + if (sflags & DB_RECNUM) + err = "no DB_RECNUM flag allowed in db_map<>"; + + return err; + + } + +}; // db_multiset<> + + +END_NS + +#endif// !_DB_STL_DB_SET_H_ diff --git a/stl/dbstl_utility.h b/stl/dbstl_utility.h new file mode 100644 index 0000000..8712341 --- /dev/null +++ b/stl/dbstl_utility.h @@ -0,0 +1,496 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#ifndef _DB_STL_UTILITY_H__ +#define _DB_STL_UTILITY_H__ + +#include "dbstl_inner_utility.h" + +START_NS(dbstl) + +// This class allows users to configure dynamically how a specific type of +// object is copied, stored, restored, and how to measure the type's +// instance size. +/** \defgroup dbstl_helper_classes dbstl helper classes +Classes of this module help to achieve various features of dbstl. +*/ +/** \ingroup dbstl_helper_classes +@{ +This class is used to register callbacks to manipulate an object of a +complex type. These callbacks are used by dbstl at runtime to +manipulate the object. + +A complex type is a type whose members are not located in a contiguous +chunk of memory. For example, the following class A is a complex type +because for any instance a of class A, a.b_ points to another object +of type B, and dbstl treats the object that a.b_ points to as part of +the data of the instance a. Hence, if the user needs to store a.b_ into +a dbstl container, the user needs to register an appropriate callback to +de-reference and store the object referenced by a.b. Similarly, the +user also needs to register callbacks to marshall an array as well as +to count the number of elements in such an array. + +class A { int m; B *p_; }; +class B { int n; }; + +The user also needs to register callbacks for +i). returning an object¡¯s size in bytes; +ii). Marshalling and unmarshalling an object; +iii). Copying a complex object and and assigning an object to another +object of the same type; +iv). Element comparison. +v). Compare two sequences of any type of objects; Measuring the length +of an object sequence and copy an object sequence. + +Several elements located in a contiguous chunk of memory form a sequence. +An element of a sequence may be a simple object located at a contigous +memory chunk, or a complex object, i.e. some of its members may contain +references (pointers) to another region of memory. It is not necessary +to store a special object to denote the end of the sequence. The callback +to traverse the constituent elements of the sequence needs to able to +determine the end of the sequence. + +Marshalling means packing the object's data members into a contiguous +chunk of memory; unmarshalling is the opposite of marshalling. In other +words, when you unmarshall an object, its data members are populated +with values from a previously marshalled version of the object. + +The callbacks need not be set to every type explicitly. . dbstl will +check if a needed callback function of this type is provided. +If one is available, dbstl will use the registered callback. If the +appropriate callback is not provided, dbstl will use reasonable defaults +to do the job. + +For returning the size of an object, the default behavior is to use the +sizeof() operator; For marshalling and unmarshalling, dbstl uses memcpy, +so the default behavior is sufficient for simple types whose data reside +in a contiguous chunk of memory; Dbstl uses uses >, == and < for +comparison operations; For char* and wchar_t * strings, dbstl already +provides the appropriate callbacks, so you do not need to register them. +In general, if the default behavior is adequate, you don't need to +register the corresponding callback. + +If you have registered proper callbacks, the DbstlElemTraits<T> can also be +used as the char_traits<T> class for std::basic_string<T, char_traits<T> >, +and you can enable your class T to form a basic_string<T, DbstlElemTraits<T>>, +and use basic_string's functionality and the algorithms to manipulate it. +*/ +template <typename T> +class _exported DbstlElemTraits : public DbstlGlobalInnerObject +{ +public: + /// \name Callback_typedefs Function callback type definitions. + /// Following are the callback function types, there is one function + /// pointer data member for each of the type, and a pair of set/get + /// functions for each function callback data member, and this is + /// the structure of this class. + //@{ + /// Assign object src to object dest. Most often assignment callback + /// is not needed - the class copy constructor is sufficient. + /// This description talks about the function of this type, rather + /// than the type itself, this is true to all the types in the group. + typedef void (*ElemAssignFunct)(T& dest, const T&src); + /// Read data from the chunk of memory pointed by srcdata, and assign + /// to the object dest. This is also called unmashall. + typedef void (*ElemRstoreFunct)(T& dest, const void *srcdata); + /// Return object elem's size in bytes. + typedef u_int32_t (*ElemSizeFunct)(const T& elem); + /// Copy object elem's data to be persisted into a memory chunk + /// referenced by dest. The dest memory is large enough. + /// elem may not reside on a + /// consecutive chunk of memory. This is also called marshal. + typedef void (*ElemCopyFunct)(void *dest, const T&elem); + typedef int (*ElemCompareFunct)(const T&a, const T&b); + /// Compares first num number of elements of sequence a and b, returns + /// negative/0/positive value if a is less/equal/greater than b. + typedef int (*SequenceNCompareFunct)(const T *a, const T *b, + size_t num); + /// Compares sequence a and b, returns negative/0/positive + /// value if a is less/equal/greater than b. + typedef int (*SequenceCompareFunct)(const T *a, const T *b); + /// Return the sequence's number of elements. + typedef u_int32_t (*SequenceLenFunct)(const T *seqs); + + /// Copy the sequence seqs's first num elements to seqd. + /// The sequence seqs of type T objects may not reside in a continuous + /// chunk of memory, but the seqd sequence points to a consecutive + /// chunk of memory large enough to hold all objects from seqs. + /// And if T is a complex type, you should register your ElemCopyFunct + /// object marshalling manipulator + typedef void (*SequenceCopyFunct)(T *seqd, const T *seqs, size_t num); + //@} + + typedef T char_type; + typedef long int_type; + + /// \name Interface compatible with std::string's char_traits. + /// Following are char_traits funcitons, which make this class + /// char_traits compatiable, so that it can be used in + /// std::basic_string template, and be manipulated by the c++ stl + /// algorithms. + //@{ + /// Assignone object to another. + static void assign(T& left, const T& right) + { + if (inst_->assign_) + inst_->assign_(left, right); + else + left = right; + } + + /// Check for equality of two objects. + static bool eq(const T& left, const T& right) + { + if (inst_->elemcmp_) + return inst_->elemcmp_(left, right) == 0; + else + return left == right; + } + + /// \brief Less than comparison. + /// + /// Returns if object left is less than object right. + static bool lt(const T& left, const T& right) + { + if (inst_->elemcmp_) + return inst_->elemcmp_(left, right) < 0; + else + return left < right; + } + + /// \brief Sequence comparison. + /// + /// Compares the first cnt number of elements in the two + /// sequences seq1 and seq2, returns negative/0/positive if seq1 is + /// less/equal/greater than seq2. + static int compare(const T *seq1, const T *seq2, size_t cnt) + { + if (inst_->seqncmp_) + return inst_->seqncmp_(seq1, seq2, cnt); + else { + for (; 0 < cnt; --cnt, ++seq1, ++seq2) + if (!eq(*seq1, *seq2)) + return (lt(*seq1, *seq2) ? -1 : +1); + } + return (0); + } + + /// Returns the number of elements in sequence seq1. Note that seq1 + /// may or may not end with a trailing '\0', it is completely user's + /// responsibility for this decision, though seq[0], seq[1],... + /// seq[length - 1] are all sequence seq's memory. + static size_t length(const T *seq) + { + assert(inst_->seqlen_ != NULL); + return (size_t)inst_->seqlen_(seq); + } + + /// Copy first cnt number of elements from seq2 to seq1. + static T * copy(T *seq1, const T *seq2, size_t cnt) + { + if (inst_->seqcpy_) + inst_->seqcpy_(seq1, seq2, cnt); + else { + T *pnext = seq1; + for (; 0 < cnt; --cnt, ++pnext, ++seq2) + assign(*pnext, *seq2); + } + return (seq1); + } + + /// Find within the first cnt elements of sequence seq the position + /// of element equal to elem. + static const T * find(const T *seq, size_t cnt, const T& elem) + { + for (; 0 < cnt; --cnt, ++seq) + if (eq(*seq, elem)) + return (seq); + return (0); + } + + /// \brief Sequence movement. + /// + /// Move first cnt number of elements from seq2 to seq1, seq1 and seq2 + /// may or may not overlap. + static T * move(T *seq1, const T *seq2, size_t cnt) + { + T *pnext = seq1; + if (seq2 < pnext && pnext < seq2 + cnt) + for (pnext += cnt, seq2 += cnt; 0 < cnt; --cnt) + assign(*--pnext, *--seq2); + else + for (; 0 < cnt; --cnt, ++pnext, ++seq2) + assign(*pnext, *seq2); + return (seq1); + } + + /// Assign first cnt number of elements of sequence seq with the + /// value of elem. + static T * assign(T *seq, size_t cnt, T elem) + { + T *pnext = seq; + for (; 0 < cnt; --cnt, ++pnext) + assign(*pnext, elem); + return (seq); + } + + static T to_char_type(const int_type& meta_elem) + { // convert metacharacter to character + return ((T)meta_elem); + } + + static int_type to_int_type(const T& elem) + { // convert character to metacharacter + return ((int_type)elem); + } + + static bool eq_int_type(const int_type& left, + const int_type& right) + { // test for metacharacter equality + return (left == right); + } + + static int_type eof() + { // return end-of-file metacharacter + return ((int_type)EOF); + } + + static int_type not_eof(const int_type& meta_elem) + { // return anything but EOF + return (meta_elem != eof() ? (int_type)meta_elem : + (int_type)!eof()); + } + + //@} + + /// Factory method to create a singeleton instance of this class. + /// The created object will be deleted by dbstl upon process exit. + inline static DbstlElemTraits *instance() + { + if (!inst_) { + inst_ = new DbstlElemTraits(); + register_global_object(inst_); + } + return inst_; + } + + /// \name Set/get functions for callback function pointers. + /// These are the setters and getters for each callback function + /// pointers. + //@{ + inline void set_restore_function(ElemRstoreFunct f) + { + restore_ = f; + } + + inline ElemRstoreFunct get_restore_function() { return restore_; } + + inline void set_assign_function(ElemAssignFunct f) + { + assign_ = f; + } + + inline ElemAssignFunct get_assign_function() { return assign_; } + + inline ElemSizeFunct get_size_function() { return size_; } + + inline void set_size_function(ElemSizeFunct f) { size_ = f; } + + inline ElemCopyFunct get_copy_function() { return copy_; } + + inline void set_copy_function(ElemCopyFunct f) { copy_ = f; } + + inline void set_sequence_len_function(SequenceLenFunct f) + { + seqlen_ = f; + } + + inline SequenceLenFunct get_sequence_len_function() { return seqlen_; } + + inline SequenceCopyFunct get_sequence_copy_function() + { + return seqcpy_; + } + + inline void set_sequence_copy_function(SequenceCopyFunct f) + { + seqcpy_ = f; + } + + inline void set_compare_function(ElemCompareFunct f) + { + elemcmp_ = f; + } + + inline ElemCompareFunct get_compare_function() + { + return elemcmp_; + } + + inline void set_sequence_compare_function(SequenceCompareFunct f) + { + seqcmp_ = f; + } + + inline SequenceCompareFunct get_sequence_compare_function() + { + return seqcmp_; + } + + inline void set_sequence_n_compare_function(SequenceNCompareFunct f) + { + seqncmp_ = f; + } + + inline SequenceNCompareFunct get_sequence_n_compare_function() + { + return seqncmp_; + } + //@} + + ~DbstlElemTraits(){} +protected: + inline DbstlElemTraits() + { + assign_ = NULL; + restore_ = NULL; + size_ = NULL; + copy_ = NULL; + seqlen_ = NULL; + seqcpy_ = NULL; + seqcmp_ = NULL; + seqncmp_ = NULL; + elemcmp_ = NULL; + } + + static DbstlElemTraits *inst_; + + // Data members to hold registered function pointers. + ElemAssignFunct assign_; + ElemRstoreFunct restore_; + ElemSizeFunct size_; + ElemCopyFunct copy_; + ElemCompareFunct elemcmp_; + SequenceCompareFunct seqcmp_; + SequenceNCompareFunct seqncmp_; + SequenceLenFunct seqlen_; + SequenceCopyFunct seqcpy_; +}; //DbstlElemTraits +//@} // dbstl_helper_classes + +template<typename T> +DbstlElemTraits<T> *DbstlElemTraits<T>::inst_ = NULL; + +/** +\ingroup dbstl_helper_classes +@{ +You can persist all bytes in a chunk of contiguous memory by constructing +an DbstlDbt object A(use malloc to allocate the required number of bytes for +A.data and copy the bytes to be stored into A.data, set other +fields as necessary) and store A into a container, e.g. db_vector<DbstlDbt>, +this stores the bytes rather than the object A into the underlying database. +The DbstlDbt class can help you avoid memory leaks, +so it is strongly recommended that you use DbstlDbt rather than Dbt class. + +DbstlDbt derives from Dbt class, and it does an deep copy on copy construction +and assignment --by calling malloc to allocate its own memory and then +copying the bytes to it; Conversely the destructor will free the memory on +destruction if the data pointer is non-NULL. The destructor assumes the +memory is allocated via malloc, hence why you are required to call +malloc to allocate memory in order to use DbstlDbt. + +DbstlDbt simply inherits all methods from Dbt with no extra +new methods except the constructors/destructor and assignment operator, so it +is easy to use. + +In practice you rarely need to use DbstlDbt +or Dbt because dbstl enables you to store any complex +objects or primitive data. Only when you need to store raw bytes, +e.g. a bitmap, do you need to use DbstlDbt. + +Hence, DbstlDbt is the right class to +use to store any object into Berkeley DB via dbstl without memory leaks. + +Don't free the memory referenced by DbstlDbt objects, it will be freed when the +DbstlDbt object is destructed. + +Please refer to the two examples using DbstlDbt in +TestAssoc::test_arbitrary_object_storage and +TestAssoc::test_char_star_string_storage member functions, +which illustrate how to correctly use DbstlDbt in order to store raw bytes. + +This class handles the task of allocating and de-allocating memory internally. +Although it can be used to store data which cannot be handled by the +DbstlElemTraits +class, in practice, it is usually more convenient to register callbacks in the +DbstlElemTraits class for the type you are storing/retrieving using dbstl. +*/ +class DbstlDbt : public Dbt +{ + inline void deep_copy(const DbstlDbt &d) + { + u_int32_t len; + if (d.get_data() != NULL && d.get_size() > 0) { + if (d.get_flags() & DB_DBT_USERMEM) + len = d.get_ulen(); + else + len = d.get_size(); + + set_data(DbstlMalloc(len)); + memcpy(get_data(), d.get_data(), len); + } + } + +public: + /// Construct an object with an existing chunk of memory of size1 + /// bytes, refered by data1, + DbstlDbt(void *data1, u_int32_t size1) : Dbt(data1, size1){} + DbstlDbt() : Dbt(){} + /// The memory will be free'ed by the destructor. + ~DbstlDbt() + { + free_mem(); + } + + /// This copy constructor does a deep copy. + DbstlDbt(const DbstlDbt &d) : Dbt(d) + { + deep_copy(d); + } + + /// The memory will be reallocated if neccessary. + inline const DbstlDbt &operator = (const DbstlDbt &d) + { + ASSIGNMENT_PREDCOND(d) + + if (d.get_data() != NULL && d.get_size() > 0) { + free_mem(); + memcpy(this, &d, sizeof(d)); + } + + deep_copy(d); + return d; + } + +protected: + /// Users don't need to call this function. + inline void free_mem() + { + if (get_data()) { + free(get_data()); + memset(this, 0, sizeof(*this)); + } + } + +}; +//@} // dbstl_help_classes +END_NS + +#endif // ! _DB_STL_UTILITY_H__ + + 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 + |