summaryrefslogtreecommitdiff
path: root/stl
diff options
context:
space:
mode:
authorZhang Qiang <qiang.z.zhang@intel.com>2012-05-29 12:22:00 +0800
committerZhang Qiang <qiang.z.zhang@intel.com>2012-05-29 12:22:00 +0800
commit02f0634ac29e19c68279e5544cac963e7f1203b8 (patch)
treeb983472f94ef063cedf866d8ecfb55939171779d /stl
parente776056ea09ba0b6d9505ced6913c9190a12d632 (diff)
downloaddb4-1.0_post.tar.gz
db4-1.0_post.tar.bz2
db4-1.0_post.zip
Sync source code from Tizen:BaseHEAD2.0_alphamaster2.0alpha1.0_post
Diffstat (limited to 'stl')
-rw-r--r--stl/dbstl_base_iterator.h498
-rw-r--r--stl/dbstl_common.in453
-rw-r--r--stl/dbstl_container.cpp520
-rw-r--r--stl/dbstl_container.h582
-rw-r--r--stl/dbstl_dbc.h1327
-rw-r--r--stl/dbstl_dbt.h803
-rw-r--r--stl/dbstl_element_ref.h873
-rw-r--r--stl/dbstl_exception.h257
-rw-r--r--stl/dbstl_inner_utility.h59
-rw-r--r--stl/dbstl_map.h3395
-rw-r--r--stl/dbstl_resource_manager.cpp1039
-rw-r--r--stl/dbstl_resource_manager.h355
-rw-r--r--stl/dbstl_set.h1583
-rw-r--r--stl/dbstl_utility.h496
-rw-r--r--stl/dbstl_vector.h3332
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
+