DB_Wrap.cs :  » Database » Berkeley-DB » BerkeleyDb » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Database » Berkeley DB 
Berkeley DB » BerkeleyDb » DB_Wrap.cs
/*
 * This software is licensed according to the "Modified BSD License",
 * where the following substitutions are made in the license template:
 * <OWNER> = Karl Waclawek
 * <ORGANIZATION> = Karl Waclawek
 * <YEAR> = 2005, 2006
 * It can be obtained from http://opensource.org/licenses/bsd-license.html.
 */

using System;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

namespace BerkeleyDb{
  /// <summary>Represents Berkeley DB database. See
  /// <see href="http://www.sleepycat.com/docs/api_c/db_class.html"/>.</summary>
  /// <remarks>Wraps a <see cref="DB"/> handle. Can be made thread-safe by specifying
  /// the <c>Db.OpenFlags.ThreadSafe</c> flag when opening the database. However, as we
  /// synchronize all calls it does not appear necessary to specify this flag. It could be
  /// used as a safe-guard since it does not impact performance much.</remarks>
  public unsafe class Db: IDisposable
  {
    protected readonly Env env;
    DbFile dbf = null;
    bool noSync = false;

    #region Hash Code

    static Random rand = new Random();

    static int GetNextHashCode() {
      lock (rand) {
        return rand.Next();
      }
    }

    int hashCode = GetNextHashCode();

    public override int GetHashCode() {
      return hashCode;
    }

    #endregion

    #region Unmanaged Resources

    protected internal readonly object rscLock = new object();
    DbRetVal releaseVal;

    // access to properly aligned types of size "native int" is atomic!
    volatile DB* dbp = null;
    GCHandle instanceHandle;

    Set<DbCursor> cursors = new Set<DbCursor>();
    Set<Sequence> sequences = new Set<Sequence>();

    // should be run in a CER, under a lock on rscLock, and not throw exceptions
    internal DbRetVal AllocateHandle(DB_ENV* evp, DbCreateFlags flags) {
      DbRetVal ret;
      lock (rscLock) {
        DB* dbp;
        ret = LibDb.db_create(out dbp, evp, flags);
        if (ret == DbRetVal.SUCCESS) {
          this.dbp = dbp;
          dbp->api_internal = (IntPtr)instanceHandle;
        }
      }
      return ret;
    }

    // should be run in a CER, under a lock on rscLock, and not throw exceptions
    DbRetVal ReleaseUnmanagedResources() {
      UInt32 flags = noSync ? unchecked((UInt32)DbConst.DB_NOSYNC) : 0;
      DB* dbp = this.dbp;
      if (dbp == null)
        return DbRetVal.SUCCESS;
      // DB->Close() could be a lengthy call, so we call Disposed() first, and the
      // CER ensures that we reach DB->Close() without external interruption.
      // This is OK because one must not use the handle after DB->Close() was called,
      // regardless of the return value.
      Disposed();
      DbRetVal ret = dbp->Close(dbp, flags);
      return ret;
    }

    // requires synchronization lock on rscLock
    internal bool InsertSequence(Sequence seq) {
      return sequences.Insert(seq);
    }

    // requires synchronization lock on rscLock
    internal bool RemoveSequence(Sequence seq) {
      return sequences.Remove(seq);
    }

    // requires synchronization lock on rscLock
    internal bool InsertCursor(DbCursor dbc) {
      return cursors.Insert(dbc);
    }

    // requires synchronization lock on rscLock
    internal bool RemoveCursor(DbCursor dbc) {
      return cursors.Remove(dbc);
    }

    #endregion

    #region Construction, Finalization

    // also called from environment
    internal Db(Env env) {
      this.env = env;
      // so that callbacks can refer back to the Db instance
      instanceHandle = GCHandle.Alloc(this, GCHandleType.WeakTrackResurrection);
    }

    public Db(DbCreateFlags flags) {
      // Db will have private environment
      env = new Env();
      // so that callbacks can refer back to the Db instance
      instanceHandle = GCHandle.Alloc(this, GCHandleType.WeakTrackResurrection);
      DbRetVal ret;
      // do not need to lock environment, as it is not publicly visible yet
      RuntimeHelpers.PrepareConstrainedRegions();
      try { }
      finally {
        ret = AllocateHandle(null, flags);
        if (ret == DbRetVal.SUCCESS)
          env.SetOwnerDatabase(this);
      }
      Util.CheckRetVal(ret);
    }

    public const string disposedStr = "Database handle closed.";

    [CLSCompliant(false)]
    protected internal DB* CheckDisposed() {
      // avoid multiple volatile memory access
      DB* dbp = this.dbp;
      if (dbp == null)
        throw new ObjectDisposedException(disposedStr);
      return dbp;
    }

    // does not check for dbp == null!
    void Disposed() {
      dbp = null;
      // unregister with resource manager
      env.RemoveDatabase(this);
    }

    public bool IsDisposed {
      get { return dbp == null; }
    }

    internal void DisposeCursors(bool disposing) {
      if (cursors != null) {
        int iter = cursors.StartIter();
        while (cursors.MoveNext(ref iter)) {
          DbCursor dbc = cursors.Get(iter);
          // dbc removes itself from cursors
          dbc.Dispose(disposing);
          if (disposing)
            GC.SuppressFinalize(dbc);
        }
      }
    }

    void DisposeDependents(bool disposing) {
      // release sequences
      if (sequences != null) {
        int iter = sequences.StartIter();
        while (sequences.MoveNext(ref iter)) {
          Sequence seq = sequences.Get(iter);
          // seq removes itself from sequences
          seq.Dispose(disposing);
          if (disposing)
            GC.SuppressFinalize(seq);
        }
      }
      // release cursors
      DisposeCursors(disposing);
    }

    // when overriding, call base method at end (using finally clause)
    internal protected virtual void Dispose(bool disposing) {
      lock (rscLock) {
        RuntimeHelpers.PrepareConstrainedRegions();
        try { }
        finally {
          DisposeDependents(disposing);
          releaseVal = ReleaseUnmanagedResources();
        }
        if (instanceHandle.IsAllocated)
          instanceHandle.Free();
      }
    }

    public DbRetVal ReleaseVal {
      get { return releaseVal; }
    }

    #endregion

    #region IDisposable Members

    public void Dispose() {
      // always lock environment first to avoid deadlocks
      lock (env.rscLock) {
        Dispose(true);
      }
      GC.SuppressFinalize(this);
    }

    #endregion

    #region Helpers

    // for synchronizing access to instance variables while in a callback
    protected internal object callBackLock = new object();
    // callback instance variables
    protected internal byte[] callBackBuffer1 = new byte[0];
    protected internal byte[] callBackBuffer2 = new byte[0];

    // for synchronize access to instance variables used in a call
    protected internal object callLock = new object();
    // instance variables used in Db calls
    protected internal byte[] callBuffer = new byte[0];

    internal DB_ENV* GetEvp() {
      return dbp->GetEnv(dbp);
    }

    #endregion

    #region Public Operations & Properties

    public Env GetEnv() {
      return env;
    }

    /// <summary>Do not flush cached information to disk when closing database.</summary>
    public bool NoSync {
      get { return noSync; }
      set { noSync = value; }
    }

    public bool IsSecondary {
      get {
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          return dbp->IsSecondary;
        }
      }
    }

    public DbFile Dbf {
      get { return dbf; }
    }

    DbFile Open(
      DB_TXN* txp,
      byte[] fBytes,
      byte[] dBytes,
      DbType type,
      OpenFlags flags,
      int mode)
    {
      lock (rscLock) {
        if (dbf != null)  // database already open
          return null;
        DB* dbp = CheckDisposed();
        DbRetVal ret;
        fixed (byte* fp = fBytes, dp = dBytes) {
          ret = dbp->Open(dbp, txp, fp, dp, type, unchecked((UInt32)flags), mode);
        }
        Util.CheckRetVal(ret);
        if (type == DbType.Unknown)
          type = GetDbType(dbp);
        switch (type) {
          case DbType.BTree:
            dbf = new DbBTree(this);
            break;
          case DbType.Hash:
            dbf = new DbHash(this);
            break;
          case DbType.Queue:
            dbf = new DbQueue(this);
            break;
          case DbType.Recno:
            dbf = new DbRecNo(this);
            break;
          default:
            throw new BdbException(
              string.Format("Unrecognized database type: '{0}'.", type.ToString()));
        }
        return dbf;
      }
    }

    public DbFile Open(
      Txn txn,
      string file,
      string database,
      DbType type,
      OpenFlags flags,
      int mode) 
    {
      byte[] fBytes = null;
      Util.StrToUtf8(file, ref fBytes);
      byte[] dBytes = null;
      Util.StrToUtf8(database, ref dBytes);

      DbFile result;
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          result = Open(txp, fBytes, dBytes, type, flags, mode);
        }
      }
      else
        result = Open(null, fBytes, dBytes, type, flags, mode);
      if (result == null)
        throw new BdbException(string.Format("Database already open: {0}.", file));
      return result;
    }

    public void Close() {
      Dispose();
    }

    // use for checking the type of an unknown open database
    [CLSCompliant(false)]
    public static DbType GetDbType(DB* dbp) {
      if (dbp == null)
        throw new ArgumentNullException("dbp");
      DbType result;
      DbRetVal ret = dbp->GetDbType(dbp, out result);
      Util.CheckRetVal(ret);
      return result;
    }

    // likely called from call-back - must not throw exception
    public void Error(int errno, string errMsg) {
      byte[] errBytes = null;
      try {
        Util.StrToUtf8(errMsg, ref errBytes);
      }
      catch { }
      fixed (byte* errStr = errBytes) {
        // don't lock, if the call-back comes from a different thread we might dead-lock
        DB* dbp = this.dbp;
        if (dbp == null)
          return;
        dbp->Err(dbp, errno, errStr);
      }
    }

    // likely called from call-back - must not throw exception
    public void Error(string errMsg) {
      byte[] errBytes = null;
      try {
        Util.StrToUtf8(errMsg, ref errBytes);
      }
      catch { }
      fixed (byte* errStr = errBytes) {
        // don't lock, if the call-back comes from a different thread we might dead-lock
        DB* dbp = this.dbp;
        if (dbp == null)
          return;
        dbp->Errx(dbp, errStr);
      }
    }

    DbCacheFile cacheFile = null;

    public DbCacheFile CacheFile {
      get {
        if (cacheFile == null) {
          DbRetVal ret;
          DB_MPOOLFILE* mpf;
          uint pageSize;
          DbCacheFile dbcf = new DbCacheFile(this);
          lock (rscLock) {
            DB* dbp = CheckDisposed();
#if BDB_4_3_29
            mpf = dbp->mpf;
#endif
#if BDB_4_5_20
            mpf = dbp->GetMpf(dbp);
#endif
            ret = dbp->GetPageSize(dbp, out pageSize);
          }
          Util.CheckRetVal(ret);
          dbcf.mpf = mpf;
          dbcf.pageSize = pageSize;
          cacheFile = dbcf;
        }
        return cacheFile;
      }
    }

    public void Remove(string file, string database) {
      byte[] fBytes = null;
      Util.StrToUtf8(file, ref fBytes);
      byte[] dBytes = null;
      Util.StrToUtf8(database, ref dBytes);
      
      
      DbRetVal ret;
      // always lock environment first to avoid deadlocks
      lock (env.rscLock) {
        lock (rscLock) {
          RuntimeHelpers.PrepareConstrainedRegions();
          try { }
          finally {
            DB* dbp = CheckDisposed();
            // DB->Remove() could be a lengthy call, so we call Disposed() first, and the
            // CER ensures that we reach DB->Remove() without external interruption.
            // This is OK because one must not use the handle after DB->Remove() was called,
            // regardless of the return value.
            Disposed();  // there are no dependents to be disposed of
            fixed (byte* fp = fBytes, dp = dBytes) {
              ret = dbp->Remove(dbp, fp, dp, 0);
            }
          }
        }
      }
      GC.SuppressFinalize(this);
      Util.CheckRetVal(ret);
    }

    public void Rename(string file, string database, string newName) {
      byte[] fBytes = null;
      Util.StrToUtf8(file, ref fBytes);
      byte[] dBytes = null;
      Util.StrToUtf8(database, ref dBytes);
      byte[] nBytes = null;
      Util.StrToUtf8(newName, ref nBytes);

      DbRetVal ret;
      // always lock environment first to avoid deadlocks
      lock (env.rscLock) {
        lock (rscLock) {
          RuntimeHelpers.PrepareConstrainedRegions();
          try { }
          finally {
            DB* dbp = CheckDisposed();
            // DB->Rename() could be a lengthy call, so we call Disposed() first, and the
            // CER ensures that we reach DB->Rename() without external interruption.
            // This is OK because one must not use the handle after DB->Rename() was called,
            // regardless of the return value.
            Disposed();  // there are no dependents to be disposed of
            fixed (byte* fp = fBytes, dp = dBytes, np = nBytes) {
              ret = dbp->Rename(dbp, fp, dp, np, 0);
            }
          }
        }
      }
      GC.SuppressFinalize(this);
      Util.CheckRetVal(ret);
    }

    // Maybe it is better to use the Upgrade utility instead.
    public void Upgrade(string file, UpgradeFlags flags) {
      byte[] fBytes = null;
      Util.StrToUtf8(file, ref fBytes);
      DbRetVal ret;
      lock (rscLock) {
        DB* dbp = CheckDisposed();
        fixed (byte* fp = fBytes) {
          ret = dbp->Upgrade(dbp, fp, (UInt32)flags);
        }
      }
      Util.CheckRetVal(ret);
    }

    #region Verify

    public delegate DbRetVal VerifyCallFcn(Db db, string arg);

    // keep stream & delegate alive
    Stream verifyStream = null;
    VerifyCallback verifyCall = null;

    static DbRetVal VerifyWrapStream(IntPtr handle, byte* arg) {
      try {
        GCHandle gch = (GCHandle)handle;
        Db db = (Db)gch.Target;

        int argSize = Util.ByteStrLen(arg);
        lock (db.callBackLock) {
          // copy into byte buffer
          if (argSize > db.callBackBuffer1.Length)
            db.callBackBuffer1 = new byte[argSize];
          if (arg != null)
            Marshal.Copy((IntPtr)arg, db.callBackBuffer1, 0, argSize);
          // write to verify stream
          db.verifyStream.Write(db.callBackBuffer1, 0, argSize);
        }
        return DbRetVal.SUCCESS;
      }
      catch (Exception ex) {
        Trace.WriteLine(ex.Message, "Verify Call");
        return DbRetVal.VERIFY_FAILED;
      }
    }

    public void Verify(string file, string database, Stream dumpStream, VerifyFlags flags) {
      byte[] fBytes = null;
      Util.StrToUtf8(file, ref fBytes);
      byte[] dBytes = null;
      Util.StrToUtf8(database, ref dBytes);
      IntPtr cb = IntPtr.Zero;

      DbRetVal ret;
      // always lock environment first to avoid deadlocks
      lock (env.rscLock) {
        lock (rscLock) {
          verifyStream = dumpStream;
          if (dumpStream != null) {
            verifyCall = VerifyWrapStream;
            cb = Marshal.GetFunctionPointerForDelegate(verifyCall);
          }
          else
            verifyCall = null;

          RuntimeHelpers.PrepareConstrainedRegions();
          try { }
          finally {
            DB* dbp = CheckDisposed();
            // LibDb.db_verify_internal() could be a lengthy call, so we call Disposed()
            // first, and the CER ensures that we reach LibDb.Verify() without external
            // interruption. This is OK because one must not use the handle after
            // LibDb.db_verify_internal() was called, regardless of the return value.
            Disposed();  // there are no dependents to be disposed of
            fixed (byte* fp = fBytes, dp = dBytes) {
              ret = LibDb.db_verify_internal(dbp, fp, dp, (IntPtr)instanceHandle, cb, unchecked((UInt32)flags));
            }
          }
          verifyStream = null;
        }
      }
      GC.SuppressFinalize(this);
      Util.CheckRetVal(ret);
    }

    #endregion Verify

    // TODO do we need the capability to set custom memory allocation functions for a database

    #endregion

    #region Public General Configuration

    public DbFlags GetFlags() {
      uint value;
      DbRetVal ret;
      lock (rscLock) {
        DB* dbp = CheckDisposed();
        ret = dbp->GetFlags(dbp, out value);
      }
      Util.CheckRetVal(ret);
      return unchecked((DbFlags)value);
    }

    public void SetFlags(DbFlags value) {
      DbRetVal ret;
      lock (rscLock) {
        DB* dbp = CheckDisposed();
        ret = dbp->SetFlags(dbp, unchecked((uint)value));
      }
      Util.CheckRetVal(ret);
    }

    public CacheSize CacheSize {
      get {
        CacheSize value;
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->GetCacheSize(dbp, out value.gigaBytes, out value.bytes, out value.numCaches);
        }
        Util.CheckRetVal(ret);
        return value;
      }
      set {
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->SetCacheSize(dbp, value.gigaBytes, value.bytes, value.numCaches);
        }
        Util.CheckRetVal(ret);
      }
    }

    public int PageSize {
      get {
        uint value;
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->GetPageSize(dbp, out value);
        }
        Util.CheckRetVal(ret);
        return unchecked((int)value);
      }
      set {
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->SetPageSize(dbp, unchecked((uint)value));
        }
        Util.CheckRetVal(ret);
      }
    }

    public int LOrder {
      get {
        int value;
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->GetLOrder(dbp, out value);
        }
        Util.CheckRetVal(ret);
        return value;
      }
      set {
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->SetLOrder(dbp, value);
        }
        Util.CheckRetVal(ret);
      }
    }

    public EncryptMode EncryptFlags {
      get {
        UInt32 value;
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->GetEncryptFlags(dbp, out value);
        }
        Util.CheckRetVal(ret);
        return (EncryptMode)value;
      }
    }

    public void SetEncryption(string password, EncryptMode mode) {
      byte[] pwdBytes = null;
      Util.StrToUtf8(password, ref pwdBytes);
      DbRetVal ret;
      lock (rscLock) {
        DB* dbp = CheckDisposed();
        fixed (byte* pwd = pwdBytes) {
          ret = dbp->SetEncrypt(dbp, pwd, (UInt32)mode);
        }
      }
      Util.CheckRetVal(ret);
    }

    #region DupCompare Callback

    [CLSCompliant(false)]
    public delegate int DupCompareFastFcn(Db db, ref DBT appData, ref DBT dbData);
    public delegate int DupCompareFcn(Db db, ref DbEntry appdata, ref DbEntry dbData);

    // keep delegates alive
    DupCompareFastFcn dupCompareFast = null;
    DupCompareFcn dupCompareCLS = null;
    DB.DupCompareFcn dupCompare = null;

    void SetDupCompare(DB.DupCompareFcn value) {
      IntPtr dupCmp = IntPtr.Zero;
      if (value != null)
        dupCmp = Marshal.GetFunctionPointerForDelegate(value);
      DbRetVal ret;
      lock (rscLock) {
        DB* dbp = CheckDisposed();
        ret = dbp->SetDupCompare(dbp, dupCmp);
      }
      Util.CheckRetVal(ret);
    }

    static int DupCompareWrapFast(DB* dbp, ref DBT appData, ref DBT dbData) {
      Db db = null;
      try {
        db = Util.GetDb(dbp);
        return db.dupCompareFast(db, ref appData, ref dbData);
      }
      catch (Exception ex) {
        Trace.WriteLine(ex.Message, "DupCompare");
        if (db != null)
          db.Error((int)DbRetVal.DUPCOMP_FAILED, ex.Message);
        else
          dbp->Err(dbp, (int)DbRetVal.DUPCOMP_FAILED, null);
        return 0;
      }
    }

    static int DupCompareWrapCLS(DB* dbp, ref DBT appData, ref DBT dbData) {
      Db db = null;
      try {
        db = Util.GetDb(dbp);
        lock(db.callBackLock) {
          // construct DbEntry for appData
          int size = unchecked((int)appData.size);
          if (size > db.callBackBuffer1.Length)
            db.callBackBuffer1 = new byte[size];
          Marshal.Copy((IntPtr)appData.data, db.callBackBuffer1, 0, size);
          DbEntry appEntry = DbEntry.InOut(db.callBackBuffer1, 0, size);
          // appEntry.dbt.flags = appData->flags; // not used
          // appEntry.dbt.dlen = appData->dlen;   // not used
          // appEntry.dbt.doff = appData->doff;   // not used

          // construct DbEntry for dbData
          size = unchecked((int)dbData.size);
          if (size > db.callBackBuffer2.Length)
            db.callBackBuffer2 = new byte[size];
          Marshal.Copy((IntPtr)dbData.data, db.callBackBuffer2, 0, size);
          DbEntry dbEntry = DbEntry.InOut(db.callBackBuffer2, 0, size);
          // dbEntry.dbt.flags = dbData->flags; // not used
          // dbEntry.dbt.dlen = dbData->dlen;   // not used
          // dbEntry.dbt.doff = dbData->doff;   // not used

          // call CLS compliant delegate - we assume it is not null
          return db.dupCompareCLS(db, ref appEntry, ref dbEntry);
        }
      }
      catch (Exception ex) {
        Trace.WriteLine(ex.Message, "DupCompare");
        if (db != null)
          db.Error((int)DbRetVal.DUPCOMP_FAILED, ex.Message);
        else
          dbp->Err(dbp, (int)DbRetVal.DUPCOMP_FAILED, null);
        return 0;
      }
    }

    // delegate should be thread-safe
    [CLSCompliant(false)]
    public DupCompareFastFcn DupCompareFast {
      get { return dupCompareFast; }
      set {
        DB.DupCompareFcn dupComp = null;
        if (value != null)
          dupComp = DupCompareWrapFast;
        SetDupCompare(dupComp);
        dupCompareFast = value;
        dupCompareCLS = null;
        dupCompare = dupComp;
      }
    }

    // delegate should be thread-safe
    public DupCompareFcn DupCompare {
      get { return dupCompareCLS; }
      set {
        DB.DupCompareFcn dupComp = null; 
        if (value != null)
          dupComp = DupCompareWrapCLS;
        SetDupCompare(dupComp);
        dupCompareFast = null;
        dupCompareCLS = value;
        dupCompare = dupComp;
      }
    }

    #endregion DupCompare Callback

    public string ErrorPrefix {
      get { return env.ErrorPrefix; }
      set { env.ErrorPrefix = value; }
    }

    [CLSCompliant(false)]
    public Env.ErrCallFastFcn ErrorCallFast {
      get { return env.ErrorCallFast; }
      set { env.ErrorCallFast = value; }
    }

    public Env.ErrCallFcn ErrorCall {
      get { return env.ErrorCall; }
      set { env.ErrorCall = value; }
    }

    public Stream ErrorStream {
      get { return env.ErrorStream; }
      set { env.ErrorStream = value; }
    }

    [CLSCompliant(false)]
    public Env.MsgCallFastFcn MessageCallFast {
      get { return env.MessageCallFast; }
      set { env.MessageCallFast = value; }
    }

    public Env.MsgCallFcn MessageCall {
      get { return env.MessageCall; }
      set { env.MessageCall = value; }
    }

    public Stream MessageStream {
      get { return env.MessageStream; }
      set { env.MessageStream = value; }
    }

    #region Feedback Call

    public delegate void FeedbackFcn(Db db, int opcode, int percent);

    // keep delegates alive
    FeedbackFcn feedbackCLS = null;
    DB.FeedbackFcn feedback = null;

    void SetFeedback(DB.FeedbackFcn value) {
      IntPtr fb = IntPtr.Zero;
      if (value != null)
        fb = Marshal.GetFunctionPointerForDelegate(value);
      DbRetVal ret;
      lock (rscLock) {
        DB* dbp = CheckDisposed();
        ret = dbp->SetFeedback(dbp, fb);
      }
      Util.CheckRetVal(ret);
    }

    static void FeedbackWrapCLS(DB* dbp, int opcode, int percent) {
      Db db = null;
      try {
        db = Util.GetDb(dbp);
        db.feedbackCLS(db, opcode, percent);
      }
      catch (Exception ex) {
        Trace.WriteLine(ex.Message, "Feedback");
        if (db != null)
          db.Error((int)DbRetVal.FEEDBACK_FAILED, ex.Message);
        else
          dbp->Err(dbp, (int)DbRetVal.FEEDBACK_FAILED, null);
      }
    }

    // delegate should be thread-safe
    public FeedbackFcn FeedbackCall {
      get { return feedbackCLS; }
      set {
        DB.FeedbackFcn fdback = null;
        if (value != null)
          fdback = FeedbackWrapCLS;
        SetFeedback(fdback);
        feedbackCLS = value;
        feedback = fdback;
      }
    }

    #endregion

#if BDB_4_3_29

    public Env.PanicCallFcn PanicCall {
      get { return env.PanicCall; }
      set { env.PanicCall = value; }
    }

#endif

    #endregion Public General Configuration

    #region Public BTree/RecNo Configuration

    #region BTreeCompare Callback

    [CLSCompliant(false)]
    public delegate int BtCompareFastFcn(Db db, ref DBT dbt1, ref DBT dbt2);
    public delegate int BtCompareFcn(Db db, ref DbEntry entry1, ref DbEntry entry2);

    // keep delegates alive
    BtCompareFastFcn btCompareFast = null;
    BtCompareFcn btCompareCLS = null;
    DB.BtCompareFcn btCompare = null;

    void SetBtCompare(DB.BtCompareFcn value) {
      IntPtr btCmp = IntPtr.Zero;
      if (value != null)
        btCmp = Marshal.GetFunctionPointerForDelegate(value);
      DbRetVal ret;
      lock (rscLock) {
        DB* dbp = CheckDisposed();
        ret = dbp->SetBtCompare(dbp, btCmp);
      }
      Util.CheckRetVal(ret);
    }

    static int BtCompareWrapFast(DB* dbp, ref DBT dbt1, ref DBT dbt2) {
      Db db = null;
      try {
        db = Util.GetDb(dbp);
        return db.btCompareFast(db, ref dbt1, ref dbt2);
      }
      catch (Exception ex) {
        Trace.WriteLine(ex.Message, "BtCompare");
        if (db != null)
          db.Error((int)DbRetVal.BTCOMP_FAILED, ex.Message);
        else
          dbp->Err(dbp, (int)DbRetVal.BTCOMP_FAILED, null);
        return 0;
      }
    }

    static int BtCompareWrapCLS(DB* dbp, ref DBT dbt1, ref DBT dbt2) {
      Db db = null;
      try {
        db = Util.GetDb(dbp);
        lock(db.callBackLock) {
          // construct DbEntry for dbt1
          int size = unchecked((int)dbt1.size);
          if (size > db.callBackBuffer1.Length)
            db.callBackBuffer1 = new byte[size];
          Marshal.Copy((IntPtr)dbt1.data, db.callBackBuffer1, 0, size);
          DbEntry entry1 = DbEntry.InOut(db.callBackBuffer1, 0, size);

          // construct DbEntry for dbData
          size = unchecked((int)dbt2.size);
          if (size > db.callBackBuffer2.Length)
            db.callBackBuffer2 = new byte[size];
          Marshal.Copy((IntPtr)dbt2.data, db.callBackBuffer2, 0, size);
          DbEntry entry2 = DbEntry.InOut(db.callBackBuffer2, 0, size);

          // call CLS compliant delegate - we assume it is not null
          return db.btCompareCLS(db, ref entry1, ref entry2);
        }
      }
      catch (Exception ex) {
        Trace.WriteLine(ex.Message, "BtCompare");
        if (db != null)
          db.Error((int)DbRetVal.BTCOMP_FAILED, ex.Message);
        else
          dbp->Err(dbp, (int)DbRetVal.BTCOMP_FAILED, null);
        return 0;
      }
    }

    // delegate should be thread-safe
    [CLSCompliant(false)]
    public BtCompareFastFcn BTreeCompareFast {
      get { return btCompareFast; }
      set {
        DB.BtCompareFcn btComp = null;
        if (value != null)
          btComp = BtCompareWrapFast;
        SetBtCompare(btComp);
        btCompareFast = value;
        btCompareCLS = null;
        btCompare = btComp;
      }
    }

    // delegate should be thread-safe
    public BtCompareFcn BTreeCompare {
      get { return btCompareCLS; }
      set {
        DB.BtCompareFcn btComp = null;
        if (value != null)
          btComp = BtCompareWrapCLS;
        SetBtCompare(btComp);
        btCompareFast = null;
        btCompareCLS = value;
        btCompare = btComp;
      }
    }

    #endregion BTreeCompare Callback

    #region BTreePrefix Callback

    [CLSCompliant(false)]
    public delegate uint BtPrefixFastFcn(Db db, ref DBT dbt1, ref DBT dbt2);
    public delegate int BtPrefixFcn(Db db, ref DbEntry entry1, ref DbEntry entry2);

    // keep delegates alive
    BtPrefixFastFcn btPrefixFast = null;
    BtPrefixFcn btPrefixCLS = null;
    DB.BtPrefixFcn btPrefix = null;

    void SetBtPrefix(DB.BtPrefixFcn value) {
      IntPtr btPrefix = IntPtr.Zero;
      if (value != null)
        btPrefix = Marshal.GetFunctionPointerForDelegate(value);
      DbRetVal ret;
      lock (rscLock) {
        DB* dbp = CheckDisposed();
        ret = dbp->SetBtPrefix(dbp, btPrefix);
      }
      Util.CheckRetVal(ret);
    }

    static uint BtPrefixWrapFast(DB* dbp, ref DBT dbt1, ref DBT dbt2) {
      Db db = null;
      try {
        db = Util.GetDb(dbp);
        return db.btPrefixFast(db, ref dbt1, ref dbt2);
      }
      catch (Exception ex) {
        Trace.WriteLine(ex.Message, "BtPrefix");
        if (db != null)
          db.Error((int)DbRetVal.BTPREFIX_FAILED, ex.Message);
        else
          dbp->Err(dbp, (int)DbRetVal.BTPREFIX_FAILED, null);
        return 0;
      }
    }

    static uint BtPrefixWrapCLS(DB* dbp, ref DBT dbt1, ref DBT dbt2) {
      Db db = null;
      try {
        db = Util.GetDb(dbp);
        int ret;
        lock(db.callBackLock) {
          // construct DbEntry for dbt1
          DbEntry entry1;
          int size = unchecked((int)dbt1.size);
          if (size > db.callBackBuffer1.Length)
            db.callBackBuffer1 = new byte[size];
          if (dbt1.data != null && dbt1.size != 0) {
            Marshal.Copy((IntPtr)dbt1.data, db.callBackBuffer1, 0, size);
            entry1 = DbEntry.InOut(db.callBackBuffer1, 0, size);
          }
          else
            entry1 = DbEntry.InOut(db.callBackBuffer1, 0, 0);


          // construct DbEntry for dbData
          DbEntry entry2;
          size = unchecked((int)dbt2.size);
          if (size > db.callBackBuffer2.Length)
            db.callBackBuffer2 = new byte[size];
          if (dbt2.data != null && dbt2.size != 0) {
            Marshal.Copy((IntPtr)dbt2.data, db.callBackBuffer2, 0, size);
            entry2 = DbEntry.InOut(db.callBackBuffer2, 0, size);
          }
          else
            entry2 = DbEntry.InOut(db.callBackBuffer2, 0, 0);

          // call CLS compliant delegate - we assume it is not null
          ret = db.btPrefixCLS(db, ref entry1, ref entry2);
        }
        return unchecked((uint)ret);
      }
      catch (Exception ex) {
        Trace.WriteLine(ex.Message, "BtPrefix");
        if (db != null)
          db.Error((int)DbRetVal.BTPREFIX_FAILED, ex.Message);
        else
          dbp->Err(dbp, (int)DbRetVal.BTPREFIX_FAILED, null);
        return 0;
      }
    }

    // delegate should be thread-safe
    [CLSCompliant(false)]
    public BtPrefixFastFcn BTreePrefixFast {
      get { return btPrefixFast; }
      set {
        DB.BtPrefixFcn btPrefix = null;
        if (value != null)
          btPrefix = BtPrefixWrapFast;
        SetBtPrefix(btPrefix);
        btPrefixFast = value;
        btPrefixCLS = null;
        this.btPrefix = btPrefix;
      }
    }

    // delegate should be thread-safe
    public BtPrefixFcn BTreePrefix {
      get { return btPrefixCLS; }
      set {
        DB.BtPrefixFcn btPrefix = null;
        if (value != null)
          btPrefix = BtPrefixWrapCLS;
        SetBtPrefix(btPrefix);
        btPrefixFast = null;
        btPrefixCLS = value;
        this.btPrefix = btPrefix;
      }
    }

    #endregion BTreePrefix Callback

    public int BTreeMinKey {
      get {
        uint value;
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->GetBtMinKey(dbp, out value);
        }
        Util.CheckRetVal(ret);
        return unchecked((int)value);
      }
      set {
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->SetBtMinKey(dbp, unchecked((uint)value));
        }
        Util.CheckRetVal(ret);
      }
    }

#if BDB_4_3_29

    public int BTreeMaxKey {
      set {
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->SetBtMaxKey(dbp, unchecked((UInt32)value));
        }
        Util.CheckRetVal(ret);
      }
    }

#endif

    #endregion

    #region Public RecNo/Queue Configuration

    #region AppendRecno Call

    [CLSCompliant(false)]
    public delegate AppendStatus AppendRecnoFastFcn(Db db, ref DBT data, UInt32 recno);
    public delegate AppendStatus AppendRecnoFcn(Db db, ref DbEntry data, int recno);

    // keep delegates alive
    AppendRecnoFastFcn appendRecnoFast = null;
    AppendRecnoFcn appendRecnoCLS = null;
    DB.AppendRecnoFcn appendRecno = null;

    void SetAppendRecno(DB.AppendRecnoFcn value) {
      IntPtr ar = IntPtr.Zero;
      if (value != null)
        ar = Marshal.GetFunctionPointerForDelegate(value);
      DbRetVal ret;
      lock (rscLock) {
        DB* dbp = CheckDisposed();
        ret = dbp->SetAppendRecno(dbp, ar);
      }
      Util.CheckRetVal(ret);
    }

    static DbRetVal AppendRecnoWrapFast(DB* dbp, ref DBT data, UInt32 recno) {
      try {
        Db db = Util.GetDb(dbp);
        AppendStatus aps = db.appendRecnoFast(db, ref data, recno);
        return aps == AppendStatus.Failure ? DbRetVal.APPEND_RECNO_FAILED : DbRetVal.SUCCESS;
      }
      catch (Exception ex) {
        Trace.WriteLine(ex.Message, "AppendRecno");
        return DbRetVal.APPEND_RECNO_FAILED;
      }
    }

    static DbRetVal AppendRecnoWrapCLS(DB* dbp, ref DBT data, UInt32 recno) {
      try {
        Db db = Util.GetDb(dbp);
        DbEntry dataEntry;
        AppendStatus aps;
        lock(db.callBackLock) {
          int size = unchecked((int)data.size);
          if (size > db.callBackBuffer1.Length)
            db.callBackBuffer1 = new byte[size];
          Marshal.Copy((IntPtr)data.data, db.callBackBuffer1, 0, size);
          dataEntry = DbEntry.InOut(db.callBackBuffer1, 0, size);
          dataEntry.dbt.flags = data.flags;
          dataEntry.dbt.dlen = data.dlen;
          dataEntry.dbt.doff = data.doff;

          // call CLS compliant delegate - we assume it is not null
          aps = db.appendRecnoCLS(db, ref dataEntry, unchecked((int)recno));
        }
        switch (aps) {
          case AppendStatus.DataUnchanged:
            return DbRetVal.SUCCESS;
          case AppendStatus.Failure:
            return DbRetVal.APPEND_RECNO_FAILED;
          case AppendStatus.DataModified: {
              // copy result back
              void* retData = data.data;
              // if the return data are larger than the input data, allocate new buffer
              if (data.size <= dataEntry.dbt.size) {
                DbRetVal ret = LibDb.os_umalloc(null, dataEntry.dbt.size, out retData);
                if (ret != DbRetVal.SUCCESS)
                  return ret;
                data.data = retData;
                data.flags |= DBT.DB_DBT_APPMALLOC;  // tell LibDb to free that memory
              }
              Marshal.Copy(dataEntry.Buffer, dataEntry.Start, (IntPtr)retData, dataEntry.Size);
              return DbRetVal.SUCCESS;
            }
          default:
            return DbRetVal.APPEND_RECNO_FAILED;
        }
      }
      catch (Exception ex) {
        Trace.WriteLine(ex.Message, "AppendRecno");
        return DbRetVal.APPEND_RECNO_FAILED;
      }
    }

    [CLSCompliant(false)]
    public AppendRecnoFastFcn AppendRecNoFast {
      get { return appendRecnoFast; }
      set {
        DB.AppendRecnoFcn apRecno = null;
        if (value != null)
          apRecno = AppendRecnoWrapFast;
        SetAppendRecno(apRecno);
        appendRecnoFast = value;
        appendRecnoCLS = null;
        appendRecno = apRecno;
      }
    }

    // delegate should be thread-safe
    public AppendRecnoFcn AppendRecno {
      get { return appendRecnoCLS; }
      set {
        DB.AppendRecnoFcn apRecno = null;
        if (value != null)
          apRecno = AppendRecnoWrapCLS;
        SetAppendRecno(apRecno);
        appendRecnoFast = null;
        appendRecnoCLS = value;
        appendRecno = apRecno;
      }
    }

    #endregion AppendRecno Call

    public int RecDelim {
      get {
        int value;
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->GetReDelim(dbp, out value);
        }
        Util.CheckRetVal(ret);
        return value;
      }
      set {
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->SetReDelim(dbp, value);
        }
        Util.CheckRetVal(ret);
      }
    }

    public int RecLen {
      get {
        uint value;
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->GetReLen(dbp, out value);
        }
        Util.CheckRetVal(ret);
        return unchecked((int)value);
      }
      set {
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->SetReLen(dbp, unchecked((uint)value));
        }
        Util.CheckRetVal(ret);
      }
    }

    public int RecPad {
      get {
        int value;
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->GetRePad(dbp, out value);
        }
        Util.CheckRetVal(ret);
        return value;
      }
      set {
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->SetRePad(dbp, value);
        }
        Util.CheckRetVal(ret);
      }
    }

    public string RecSource {
      get {
        string value;
        lock (rscLock) {
          byte* source;
          DB* dbp = CheckDisposed();
          DbRetVal ret = dbp->GetReSource(dbp, out source);
          Util.CheckRetVal(ret);
          value = Util.Utf8PtrToString(source);
        }
        return value;
      }
      set {
        byte[] sBytes = null;
        Util.StrToUtf8(value, ref sBytes);
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          fixed (byte* sp = sBytes) {
            ret = dbp->SetReSource(dbp, sp);
          }
        }
        Util.CheckRetVal(ret);
      }
    }

    public int QueueExtentSize {
      get {
        uint value;
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->GetQExtentSize(dbp, out value);
        }
        Util.CheckRetVal(ret);
        return unchecked((int)value);
      }
      set {
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->SetQExtentSize(dbp, unchecked((uint)value));
        }
        Util.CheckRetVal(ret);
      }
    }

    #endregion

    #region Public Hash Configuration

    #region Hash Callback

    [CLSCompliant(false)]
    public delegate UInt32 HHashFastFcn(Db db, byte* bytes, UInt32 length);
    public delegate int HHashFcn(Db db, byte[] bytes, int length);

    // keep delegates alive
    HHashFastFcn hHashFast = null;
    HHashFcn hHashCLS = null;
    DB.HHashFcn hHash = null;

    void SetHHash(DB.HHashFcn value) {
      IntPtr hHash = IntPtr.Zero;
      if (value != null)
        hHash = Marshal.GetFunctionPointerForDelegate(value);
      DbRetVal ret;
      lock (rscLock) {
        DB* dbp = CheckDisposed();
        ret = dbp->SetHHash(dbp, hHash);
      }
      Util.CheckRetVal(ret);
    }

    static uint HHashWrapFast(DB* dbp, void* bytes, UInt32 length) {
      if (bytes == null || length == 0)
        return 0;
      Db db = null;
      try {
        db = Util.GetDb(dbp);
        return db.hHashFast(db, (byte*)bytes, length);
      }
      catch (Exception ex) {
        Trace.WriteLine(ex.Message, "HHash");
        if (db != null)
          db.Error((int)DbRetVal.HHASH_FAILED, ex.Message);
        else
          dbp->Err(dbp, (int)DbRetVal.HHASH_FAILED, null);
        return 0;
      }
    }

    static uint HHashWrapCLS(DB* dbp, void* bytes, UInt32 length) {
      if (bytes == null || length == 0)
        return 0;
      Db db = null;
      try {
        db = Util.GetDb(dbp);
        // copy to buffer
        int hashValue;
        lock(db.callBackLock) {
          if (length > db.callBackBuffer1.Length)
            db.callBackBuffer1 = new byte[(int)length];
          Marshal.Copy((IntPtr)bytes, db.callBackBuffer1, 0, (int)length);
          // call CLS compliant delegate - we assume it is not null
          hashValue = db.hHashCLS(db, db.callBackBuffer1, (int)length);
        }
        return unchecked((uint)hashValue);
      }
      catch (Exception ex) {
        Trace.WriteLine(ex.Message, "HHash");
        if (db != null)
          db.Error((int)DbRetVal.HHASH_FAILED, ex.Message);
        else
          dbp->Err(dbp, (int)DbRetVal.HHASH_FAILED, null);
        return 0;
      }
    }

    // delegate should be thread-safe
    [CLSCompliant(false)]
    public HHashFastFcn HashFuncFast {
      get { return hHashFast; }
      set {
        DB.HHashFcn hh = null;
        if (value != null)
          hh = HHashWrapFast;
        SetHHash(hh);
        hHashFast = value;
        hHashCLS = null;
        hHash = hh;
      }
    }

    // delegate should be thread-safe
    public HHashFcn HashFunc {
      get { return hHashCLS; }
      set {
        DB.HHashFcn hh = null;
        if (value != null)
          hh = HHashWrapCLS;
        SetHHash(hh);
        hHashFast = null;
        hHashCLS = value;
        hHash = hh;
      }
    }

    #endregion Hash Callback

    public int HashFillFactor {
      get {
        uint value;
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->GetHFFactor(dbp, out value);
        }
        Util.CheckRetVal(ret);
        return unchecked((int)value);
      }
      set {
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->SetHFFactor(dbp, unchecked((uint)value));
        }
        Util.CheckRetVal(ret);
      }
    }

    public int HashNumElements {
      get {
        uint value;
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->GetHNelem(dbp, out value);
        }
        Util.CheckRetVal(ret);
        return unchecked((int)value);
      }
      set {
        DbRetVal ret;
        lock (rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->SetHNelem(dbp, unchecked((uint)value));
        }
        Util.CheckRetVal(ret);
      }
    }

    #endregion

    #region Nested Types

    [Flags]
    public enum OpenFlags: int
    {
      None = 0,
      AutoCommit = DbConst.DB_AUTO_COMMIT,
      Create = DbConst.DB_CREATE,
      Exclusive = DbConst.DB_EXCL,
#if BDB_4_5_20
      MultiVersion = DbConst.DB_MULTIVERSION,
#endif
      NoMemoryMap = DbConst.DB_NOMMAP,
      ReadOnly = DbConst.DB_RDONLY,
#if BDB_4_3_29
      DirtyRead = DbConst.DB_DIRTY_READ,
#endif
#if BDB_4_5_20
      ReadUncommitted = DbConst.DB_READ_UNCOMMITTED,
#endif
      ThreadSafe = DbConst.DB_THREAD,
      Truncate = DbConst.DB_TRUNCATE,
    }

    public enum JoinMode: int
    {
      None = 0,
      NoSort = DbConst.DB_JOIN_NOSORT
    }

    [Flags]
    public enum UpgradeFlags: int
    {
      None = 0,
      DupSort = DbFlags.DupSort
    }

    [Flags]
    public enum VerifyFlags: int
    {
      None = 0,
      DbSalvage = DbConst.DB_SALVAGE,
      Aggressive = DbConst.DB_AGGRESSIVE,
      NoOrderChk = DbConst.DB_NOORDERCHK,
      OrderChkOnly = DbConst.DB_ORDERCHKONLY,
      Printable = DbConst.DB_PRINTABLE,
    }

    public enum AppendStatus
    {
      DataModified = DbRetVal.SUCCESS,
      DataUnchanged,
      Failure
    }

    /// <summary>Abstract base class for Berkeley DB databases when opened for file access.</summary>
    public abstract unsafe class DbFileBase
    {
      protected static DbType type = DbType.Unknown;

      protected readonly Db db;

      protected DbFileBase(Db db) {
        this.db = db;
      }

      [CLSCompliant(false)]
      protected DB* CheckDisposed() {
        return db.CheckDisposed();
      }

      [CLSCompliant(false)]
      protected DB* dbp {
        get { return db.dbp; }
      }

      protected bool InsertSequence(Sequence seq) {
        return db.InsertSequence(seq);
      }

      protected bool InsertCursor(DbCursor dbc) {
        return db.InsertCursor(dbc);
      }

      public Db GetDb() {
        return db;
      }
    }

    #endregion
  }

  /// <summary>Intermediate base class for Berkeley DB databases when opened for file access.</summary>
  public abstract unsafe class DbFile: Db.DbFileBase
  {
    // store delegates for frequently used function pointer calls
    [CLSCompliant(false)]
    protected DB.GetFcn DbGet;
    [CLSCompliant(false)]
    protected DB.PGetFcn DbPGet;
    [CLSCompliant(false)]
    protected DB.PutFcn DbPut;
    [CLSCompliant(false)]
    protected DB.DelFcn DbDel;
    [CLSCompliant(false)]
    protected DB.KeyRangeFcn DbKeyRange;

    // initialize function pointer delegates
    protected void InitDelegates() {
      DbGet = dbp->Get;
      DbPGet = dbp->PGet;
      DbPut = dbp->Put;
      DbDel = dbp->Del;
      DbKeyRange = dbp->KeyRange;
    }

    internal DbFile(Db db) : base(db) {
      InitDelegates();
    }

    #region Helpers

    [CLSCompliant(false)]
    protected ReadStatus Get(DB_TXN* txp, ref DbEntry key, ref DbEntry data, uint flags) {
      DbRetVal ret;
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        fixed (byte* keyBufP = key.Buffer, dataBufP = data.Buffer) {
          key.dbt.data = keyBufP + key.Start;
          data.dbt.data = dataBufP + data.Start;
          ret = DbGet(dbp, txp, ref key.dbt, ref data.dbt, flags);
        }
      }
      switch (ret) {
        case DbRetVal.NOTFOUND: break;
        case DbRetVal.KEYEMPTY: break;
        case DbRetVal.BUFFER_SMALL: break;
        default: 
          Util.CheckRetVal(ret);
          break;
      }
      data.SetReturnType(type, flags);
      return (ReadStatus)ret;
    }

    [CLSCompliant(false)]
    protected ReadStatus PGet(DB_TXN* txp, ref DbEntry key, ref DbEntry pkey, ref DbEntry data, uint flags) {
      DbRetVal ret;
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        fixed (byte* keyBufP = key.Buffer, pkeyBufP = pkey.Buffer, dataBufP = data.Buffer) {
          key.dbt.data = keyBufP + key.Start;
          pkey.dbt.data = pkeyBufP + pkey.Start;
          data.dbt.data = dataBufP + data.Start;
          ret = DbPGet(dbp, txp, ref key.dbt, ref pkey.dbt, ref data.dbt, flags);
        }
      }
      switch (ret) {
        case DbRetVal.NOTFOUND: break;
        case DbRetVal.KEYEMPTY: break;
        case DbRetVal.BUFFER_SMALL: break;
        default:
          Util.CheckRetVal(ret);
          break;
      }
      data.SetReturnType(type, flags);
      return (ReadStatus)ret;
    }

    [CLSCompliant(false)]
    protected WriteStatus Put(DB_TXN* txp, ref DbEntry key, ref DbEntry data, uint flags) {
      DbRetVal ret;
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        fixed (byte* keyBufP = key.Buffer, dataBufP = data.Buffer) {
          key.dbt.data = keyBufP + key.Start;
          data.dbt.data = dataBufP + data.Start;
          ret = DbPut(dbp, txp, ref key.dbt, ref data.dbt, flags);
        }
      }
      switch (ret) {
        case DbRetVal.NOTFOUND: break;  // should not happen, according to the docs
        case DbRetVal.KEYEXIST: break;
        default:
          Util.CheckRetVal(ret);
          break;
      }
      return (WriteStatus)ret;
    }

    [CLSCompliant(false)]
    protected DeleteStatus Delete(DB_TXN* txp, ref DbEntry key, uint flags) {
      DbRetVal ret;
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        fixed (byte* keyBufP = key.Buffer) {
          key.dbt.data = keyBufP + key.Start;
          ret = DbDel(dbp, txp, ref key.dbt, flags);
        }
      }
      switch (ret) {
        case DbRetVal.NOTFOUND: break;
        case DbRetVal.KEYEMPTY: break;
        default:
          Util.CheckRetVal(ret);
          break;
      }
      return (DeleteStatus)ret;
    }

    #endregion

    #region Public Operations & Properties

    // fast - no call into LibDb
    public DbType DbType {
      get { return type; }
    }

    public void GetName(out string file, out string database) {
      byte* fp, dp;
      UTF8Encoding utf8 = new UTF8Encoding();
      byte[] nameBytes = null;
      int count;

      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        DbRetVal ret = dbp->GetDbName(dbp, out fp, out dp);
        Util.CheckRetVal(ret);
        count = Util.PtrToBuffer(fp, ref nameBytes);
        file = utf8.GetString(nameBytes, 0, count);
        count = Util.PtrToBuffer(dp, ref nameBytes);
      }
      database = utf8.GetString(nameBytes, 0, count);
    }

    public Db.OpenFlags DbOpenFlags {
      get {
        UInt32 value;
        DbRetVal ret;
        lock (db.rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->GetOpenFlags(dbp, out value);
        }
        Util.CheckRetVal(ret);
        return unchecked((Db.OpenFlags)value);
      }
    }

    public bool IsTransactional {
      get {
        lock (db.rscLock) {
          DB* dbp = CheckDisposed();
          int ret = dbp->GetTransactional(dbp);
          return ret != 0;
        }
      }
    }

    public bool ByteSwapped {
      get {
        int value;
        DbRetVal ret;
        lock (db.rscLock) {
          DB* dbp = CheckDisposed();
          ret = dbp->GetByteSwapped(dbp, out value);
        }
        Util.CheckRetVal(ret);
        return value != 0;
      }
    }

    #region Associate

    [CLSCompliant(false)]
    public delegate KeyGenStatus KeyGeneratorFastFcn(DbFile secondary, ref DBT key, ref DBT data, ref DBT result);
    public delegate KeyGenStatus 
    KeyGeneratorFcn(DbFile secondary, ref DbEntry key, ref DbEntry data, out DbEntry result);

    KeyGeneratorFastFcn keyGeneratorFast = null;
    KeyGeneratorFcn keyGeneratorCLS = null;
    DB.KeyGeneratorFcn keyGenerator = null;

    static unsafe DbRetVal KeyGeneratorWrapFast(DB* secondary, ref DBT key, ref DBT data, ref DBT result) {
      try {
        DbFile secDb = Util.GetDb(secondary).Dbf;
        return (DbRetVal)secDb.keyGeneratorFast(secDb, ref key, ref data, ref result);
      }
      catch (Exception ex) {
        Trace.WriteLine(ex.Message, "KeyGenerator");
        return DbRetVal.KEYGEN_FAILED;
      }
    }

    static unsafe DbRetVal KeyGeneratorWrapCLS(DB* secondary, ref DBT key, ref DBT data, ref DBT result) {
      try {
        Db db = Util.GetDb(secondary);
        DbFile dbf = db.Dbf;
        DbEntry resultEntry;
        KeyGenStatus ks;
        lock(db.callBackLock) {
          // construct DbEntry for key
          int size = unchecked((int)key.size);
          if (size > db.callBackBuffer1.Length)
            db.callBackBuffer1 = new byte[size];
          Marshal.Copy((IntPtr)key.data, db.callBackBuffer1, 0, size);
          DbEntry keyEntry = DbEntry.InOut(db.callBackBuffer1, 0, size);
          keyEntry.dbt.flags = key.flags;
          keyEntry.dbt.dlen = key.dlen;
          keyEntry.dbt.doff = key.doff;

          // construct DbEntry for data
          size = unchecked((int)data.size);
          if (size > db.callBackBuffer2.Length)
            db.callBackBuffer2 = new byte[size];
          Marshal.Copy((IntPtr)data.data, db.callBackBuffer2, 0, size);
          DbEntry dataEntry = DbEntry.InOut(db.callBackBuffer2, 0, size);
          dataEntry.dbt.flags = data.flags;
          dataEntry.dbt.dlen = data.dlen;
          dataEntry.dbt.doff = data.doff;

          // call CLS compliant delegate - we assume it is not null
          ks = dbf.keyGeneratorCLS(dbf, ref keyEntry, ref dataEntry, out resultEntry);
        }
        if (ks != KeyGenStatus.Success)
          return (DbRetVal)ks;
        if (resultEntry.Buffer == null)
          return DbRetVal.KEYGEN_FAILED;

        // copy result back
        result.flags = resultEntry.dbt.flags & ~DBT.DB_DBT_USERMEM;
        void* retData;
        DbRetVal ret = LibDb.os_umalloc(null, resultEntry.dbt.size, out retData);
        if (ret != DbRetVal.SUCCESS)
          return ret;
        Marshal.Copy(resultEntry.Buffer, resultEntry.Start, (IntPtr)retData, resultEntry.Size);
        result.data = retData;
        result.flags |= DBT.DB_DBT_APPMALLOC;  // tell LibDb to free that memory
        result.size = resultEntry.dbt.size;
        return DbRetVal.SUCCESS;
      }
      catch (Exception ex) {
        Trace.WriteLine(ex.Message, "KeyGenerator");
        return DbRetVal.KEYGEN_FAILED;
      }
    }

    [CLSCompliant(false)]
    public void Associate(DbFile secondary, KeyGeneratorFastFcn callback, AssociateFlags flags) {
      KeyGeneratorFastFcn oldKeyGenFast = secondary.keyGeneratorFast;
      DB.KeyGeneratorFcn oldKeyGen = secondary.keyGenerator;

      secondary.keyGeneratorFast = callback;
      secondary.keyGenerator = KeyGeneratorWrapFast;
      IntPtr cb = Marshal.GetFunctionPointerForDelegate(secondary.keyGenerator);

      DbRetVal ret;
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        lock (secondary.db.rscLock) {
          DB* secDbp = secondary.CheckDisposed();
          ret = dbp->Associate(dbp, null, secDbp, cb, unchecked((UInt32)flags));
          if (ret != DbRetVal.SUCCESS) {
            secondary.keyGeneratorFast = oldKeyGenFast;
            secondary.keyGenerator = oldKeyGen;
          }
          // function pointers may have been updated in BDB
          secondary.InitDelegates();
        }
      }
      Util.CheckRetVal(ret);
      secondary.keyGeneratorCLS = null;
    }

    public void Associate(DbFile secondary, KeyGeneratorFcn callback, AssociateFlags flags) {
      KeyGeneratorFcn oldKeyGenCLS = secondary.keyGeneratorCLS;
      DB.KeyGeneratorFcn oldKeyGen = secondary.keyGenerator;

      secondary.keyGeneratorCLS = callback; 
      secondary.keyGenerator = KeyGeneratorWrapCLS;
      IntPtr cb = Marshal.GetFunctionPointerForDelegate(secondary.keyGenerator);

      DbRetVal ret;
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        lock (secondary.db.rscLock) {
          DB* secDbp = secondary.CheckDisposed();
          ret = dbp->Associate(dbp, null, secDbp, cb, unchecked((UInt32)flags));
          if (ret != DbRetVal.SUCCESS) {
            secondary.keyGeneratorCLS = oldKeyGenCLS;
            secondary.keyGenerator = oldKeyGen;
          }
        }
      }
      Util.CheckRetVal(ret);
      secondary.keyGeneratorFast = null;
    }

    #endregion

    public ReadStatus Get(Txn txn, ref DbEntry key, ref DbEntry data, ReadFlags rflags) {
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return Get(txp, ref key, ref data, unchecked((uint)rflags));
        }
      }
      else
        return Get((DB_TXN*)null, ref key, ref data, unchecked((uint)rflags));
    }

    public ReadStatus GetExact(Txn txn, ref DbEntry key, ref DbEntry data, ReadFlags rflags) {
      uint flags = DbConst.DB_GET_BOTH | unchecked((uint)rflags);
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return Get(txp, ref key, ref data, flags);
        }
      }
      else
        return Get((DB_TXN*)null, ref key, ref data, flags);
    }

    public ReadStatus PGet(
      Txn txn,
      ref DbEntry key,
      ref DbEntry pkey,
      ref DbEntry data,
      ReadFlags rflags) 
    {
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return PGet(txp, ref key, ref pkey, ref data, unchecked((uint)rflags));
        }
      }
      else
        return PGet((DB_TXN*)null, ref key, ref pkey, ref data, unchecked((uint)rflags));
    }

    public ReadStatus PGetExact(
      Txn txn,
      ref DbEntry key,
      ref DbEntry pkey,
      ref DbEntry data,
      ReadFlags rflags)
    {
      uint flags = DbConst.DB_GET_BOTH | unchecked((uint)rflags);
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return PGet(txp, ref key, ref pkey, ref data, flags);
        }
      }
      else
        return PGet((DB_TXN*)null, ref key, ref pkey, ref data, flags);
    }

    public WriteStatus Put(Txn txn, ref DbEntry key, ref DbEntry data, WriteFlags wflags) {
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return Put(txp, ref key, ref data, unchecked((uint)wflags));
        }
      }
      else
        return Put((DB_TXN*)null, ref key, ref data, unchecked((uint)wflags));
    }

    public WriteStatus Put(Txn txn, ref DbEntry key, ref DbEntry data) {
      return Put(txn, ref key, ref data, WriteFlags.None);
    }

    // does not overwrite data if key already exists
    public WriteStatus PutNew(Txn txn, ref DbEntry key, ref DbEntry data, WriteFlags wflags) {
      uint flags = DbConst.DB_NOOVERWRITE | unchecked((uint)wflags);
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return Put(txp, ref key, ref data, flags);
        }
      }
      else
        return Put((DB_TXN*)null, ref key, ref data, flags);
    }

    public WriteStatus PutNew(Txn txn, ref DbEntry key, ref DbEntry data) {
      return PutNew(txn, ref key, ref data, WriteFlags.None);
    }

    public DeleteStatus Delete(Txn txn, ref DbEntry key, WriteFlags flags) {
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return Delete(txp, ref key, unchecked((uint)flags));
        }
      }
      else
        return Delete((DB_TXN*)null, ref key, unchecked((uint)flags));
    }

    public DeleteStatus Delete(Txn txn, ref DbEntry key) {
      return Delete(txn, ref key, WriteFlags.None);
    }

    public DbJoinCursor Join(DbFileCursor[] cursors, Db.JoinMode mode, int timeout) {
      int curLen = cursors.Length;
      // create null terminated list of DBC*
      DBC** curPtrList = stackalloc DBC*[curLen + 1];
      curPtrList[curLen] = null;

      DbJoinCursor joinDbc = new DbJoinCursor();
      int lockedIndx = 0;
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        // no need to lock joinDbc - not visible yet
        try {
          // fill the DBC* list, lock the cursors involved and set their "join owner"
          for (; lockedIndx < curLen; lockedIndx++)
            curPtrList[lockedIndx] = cursors[lockedIndx].JoinLock(timeout);
          // lockedIndx now holds the number of successfully locked cursors

          RuntimeHelpers.PrepareConstrainedRegions();
          try { }
          finally {
            DBC* dbcp;
            DbRetVal ret = dbp->Join(dbp, curPtrList, out dbcp, (UInt32)mode);
            Util.CheckRetVal(ret);
            joinDbc.Initialize(db, dbcp, cursors);
            InsertCursor(joinDbc);
            for (int indx = 0; indx < curLen; indx++)
              cursors[indx].owner = joinDbc;
          }
        }
        finally {
          // In case of a JoinLock() exception, unlock only those that were locked (< curIndx)
          for (int indx = 0; indx < lockedIndx; indx++)
            cursors[indx].JoinUnlock();
        }
      }
      return joinDbc;
    }

    public Sequence CreateSequence() {
      Sequence seq = new Sequence(db);
      DbRetVal ret;
      lock (db.rscLock) {
        RuntimeHelpers.PrepareConstrainedRegions();
        try { }
        finally {
          DB* dbp = CheckDisposed();
          ret = seq.AllocateHandle(dbp, 0);
          if (ret == DbRetVal.SUCCESS)
            InsertSequence(seq);
        }
      }
      Util.CheckRetVal(ret);
      return seq;
    }

    public void PrintStats(StatPrintFlags flags) {
      DbRetVal ret;
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        ret = dbp->StatPrint(dbp, unchecked((UInt32)flags));
      }
      Util.CheckRetVal(ret);
    }

    public void Sync() {
      DbRetVal ret;
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        ret = dbp->Sync(dbp, 0);
      }
      Util.CheckRetVal(ret);
    }

    int Truncate(DB_TXN* txp, uint flags) {
      UInt32 count;
      DbRetVal ret;
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        ret = dbp->Truncate(dbp, txp, out count, flags);
      }
      Util.CheckRetVal(ret);
      return unchecked((int)count);
    }

#if BDB_4_3_29

    public int Truncate(Txn txn, TruncateMode mode) {
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return Truncate(txp, unchecked((uint)mode));
        }
      }
      else
        return Truncate((DB_TXN*)null, unchecked((uint)mode));
    }

#endif

#if BDB_4_5_20

    public int Truncate(Txn txn) {
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return Truncate(txp, 0);
        }
      }
      else
        return Truncate((DB_TXN*)null, 0);
    }

#endif

    #endregion

    #region Nested Types

    [Flags]
    public enum AssociateFlags: int
    {
      None = 0,
#if BDB_4_3_29
      AutoCommit = DbConst.DB_AUTO_COMMIT,
#endif
      Create = DbConst.DB_CREATE,
#if BDB_4_5_20
      ImmutableKey = DbConst.DB_IMMUTABLE_KEY
#endif
    }

    public enum KeyGenStatus
    {
      Success = DbRetVal.SUCCESS,
      DoNotIndex = DbRetVal.DONOTINDEX,
      Failure = DbRetVal.KEYGEN_FAILED
    }

    [Flags]
    public enum ReadFlags: int
    {
      None = 0,
#if BDB_4_3_29
      AutoCommit = DbConst.DB_AUTO_COMMIT,
      Degree2 = DbConst.DB_DEGREE_2,
      DirtyRead = DbConst.DB_DIRTY_READ,
#endif
#if BDB_4_5_20
      ReadCommitted = DbConst.DB_READ_COMMITTED,
      ReadUncommitted = DbConst.DB_READ_UNCOMMITTED,
#endif
      Multiple = DbConst.DB_MULTIPLE,
      RMW = DbConst.DB_RMW
    }

    [Flags]
    public enum WriteFlags: int
    {
      None = 0,
#if BDB_4_3_29
      AutoCommit = DbConst.DB_AUTO_COMMIT
#endif
    }

    public enum StatFlags: int
    {
      None = 0,
#if BDB_4_3_29
      Degree2 = DbConst.DB_DEGREE_2,
      DirtyRead = DbConst.DB_DIRTY_READ,
#endif
#if BDB_4_5_20
      ReadCommitted = DbConst.DB_READ_COMMITTED,
      ReadUncommitted = DbConst.DB_READ_UNCOMMITTED,
#endif
      FastStat = DbConst.DB_FAST_STAT
    }

    [Flags]
    public enum StatPrintFlags: int
    {
      None = 0,
      All = DbConst.DB_STAT_ALL,
      Clear = DbConst.DB_STAT_CLEAR,
#if BDB_4_5_20
      Fast = DbConst.DB_FAST_STAT
#endif
    }

#if BDB_4_3_29

    public enum TruncateMode: int
    {
      None = 0,
      AutoCommit = DbConst.DB_AUTO_COMMIT
    }

#endif

    #endregion
  }

  /// <summary>Intermediate class needed for associating cursor classes with database classes.</summary>
  /// <typeparam name="D">Type of <see cref="DbFile"></see> subclass.</typeparam>
  /// <typeparam name="C">Type of <see cref="DbFileCursor"></see> subclass.</typeparam>
  public abstract unsafe class DbFile<D, C>: DbFile
    where D: DbFile<D, C>
    where C: DbFileCursor<C, D>, new()
  {
    protected DbFile(Db db) : base(db) { }

    C OpenCursor(DB_TXN* txp, DbFileCursor<C, D>.CreateFlags flags) {
      C dbc = new C();
      DbRetVal ret;
      lock (db.rscLock) {
        // do not need to lock dbc, as it is not publicly visible yet
        RuntimeHelpers.PrepareConstrainedRegions();
        try { }
        finally {
          DBC* dbcp;
          DB* dbp = CheckDisposed();
          ret = dbp->Cursor(dbp, txp, out dbcp, unchecked((UInt32)flags));
          if (ret == DbRetVal.SUCCESS) {
            dbc.Initialize(db, dbcp);
            InsertCursor(dbc);
          }
        }
      }
      Util.CheckRetVal(ret);
      return dbc;
    }

    public C OpenCursor(Txn txn, DbFileCursor<C, D>.CreateFlags flags) {
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return OpenCursor(txp, flags);
        }
      }
      else
        return OpenCursor((DB_TXN*)null, flags);
    }
  }

  /// <summary>Berkeley DB database opened for the Hash access method.</summary>
  public unsafe class DbHash: DbFile<DbHash, DbHashCursor>
  {
    static DbHash() {
      type = DbType.Hash;
    }

    internal DbHash(Db db) : base(db) { }

    #region Public Operations & Properties

    // does not overwrite entry if key+data already exist
    public WriteStatus PutUnique(Txn txn, ref DbEntry key, ref DbEntry data, WriteFlags wflags) {
      uint flags = DbConst.DB_NODUPDATA | unchecked((uint)wflags);
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return Put(txp, ref key, ref data, flags);
        }
      }
      else
        return Put((DB_TXN*)null, ref key, ref data, flags);
    }

    // does not overwrite entry if key+data already exist
    public WriteStatus PutUnique(Txn txn, ref DbEntry key, ref DbEntry data) {
      return PutUnique(txn, ref key, ref data, WriteFlags.None);
    }

    Stats GetStats(DB_TXN* txp, StatFlags mode) {
      Stats value;
      DB_HASH_STAT* sp;
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        RuntimeHelpers.PrepareConstrainedRegions();
        try { }
        finally {
          DbRetVal ret = dbp->HashStat(dbp, txp, out sp, unchecked((UInt32)mode));
          Util.CheckRetVal(ret);
          value.hashStats = *sp;
          LibDb.os_ufree(null, sp);
        }
      }
      return value;
    }

    public Stats GetStats(Txn txn, StatFlags mode) {
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return GetStats(txp, mode);
        }
      }
      else
        return GetStats((DB_TXN*)null, mode);
    }

    #endregion

    #region Nested Types

    // CLS compliant wrapper for DB_HASH_STAT
    public struct Stats
    {
      internal DB_HASH_STAT hashStats;

      /* Magic number. */
      public int Magic {
        get { return unchecked((int)hashStats.hash_magic); }
      }

      /* Version number. */
      public int Version {
        get { return unchecked((int)hashStats.hash_version); }
      }

      /* Metadata flags. */
      public int MetaFlags {
        get { return unchecked((int)hashStats.hash_metaflags); }
      }

      /* Number of unique keys. */
      public int NumKeys {
        get { return unchecked((int)hashStats.hash_nkeys); }
      }

      /* Number of data items. */
      public int NumData {
        get { return unchecked((int)hashStats.hash_ndata); }
      }

      /* Page size. */
      public int PageSize {
        get { return unchecked((int)hashStats.hash_pagesize); }
      }

      /* Fill factor specified at create. */
      public int FillFactor {
        get { return unchecked((int)hashStats.hash_ffactor); }
      }

      /* Number of hash buckets. */
      public int Buckets {
        get { return unchecked((int)hashStats.hash_buckets); }
      }

      /* Pages on the free list. */
      public int FreePages {
        get { return unchecked((int)hashStats.hash_free); }
      }

      /* Bytes free on bucket pages. */
      public int BucketsFree {
        get { return unchecked((int)hashStats.hash_bfree); }
      }

      /* Number of big key/data pages. */
      public int BigPages {
        get { return unchecked((int)hashStats.hash_bigpages); }
      }

      /* Bytes free on big item pages. */
      public int BigPagesFree {
        get { return unchecked((int)hashStats.hash_big_bfree); }
      }

      /* Number of overflow pages. */
      public int OverflowPages {
        get { return unchecked((int)hashStats.hash_overflows); }
      }

      /* Bytes free on ovfl pages. */
      public int OverflowPagesFree {
        get { return unchecked((int)hashStats.hash_ovfl_free); }
      }

      /* Number of dup pages. */
      public int DuplicatePages {
        get { return unchecked((int)hashStats.hash_dup); }
      }

      /* Bytes free on duplicate pages. */
      public int DuplicatePagesFree {
        get { return unchecked((int)hashStats.hash_dup_free); }
      }
    }

    #endregion
  }

  /// <summary>Berkeley DB database opened for the Queue access method.</summary>
  public unsafe class DbQueue: DbFile<DbQueue, DbQueueCursor>
  {
    static DbQueue() {
      type = DbType.Queue;
    }

    internal DbQueue(Db db): base(db) { }

    #region Public Operations & Properties

    // does not need to handle multiple return records - because of DB_CONSUME flags
    public ReadStatus Consume(Txn txn, ref DbEntry key, ref DbEntry data, ReadFlags rflags, bool wait) {
      uint flags;
      if (wait)
        flags = DbConst.DB_CONSUME_WAIT;
      else
        flags = DbConst.DB_CONSUME;
      flags |= unchecked((uint)rflags);
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return Get(txp, ref key, ref data, flags);
        }
      }
      else
        return Get((DB_TXN*)null, ref key, ref data, flags);
    }

    public WriteStatus Append(Txn txn, ref DbEntry key, ref DbEntry data, WriteFlags wflags) {
      uint flags = DbConst.DB_APPEND | unchecked((uint)wflags);
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return Put(txp, ref key, ref data, flags);
        }
      }
      else
        return Put((DB_TXN*)null, ref key, ref data, flags);
    }

    public WriteStatus Append(Txn txn, ref DbEntry key, ref DbEntry data) {
      return Append(txn, ref key, ref data, WriteFlags.None);
    }

    Stats GetStats(DB_TXN* txp, StatFlags mode) {
      Stats value;
      DB_QUEUE_STAT* sp;
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        RuntimeHelpers.PrepareConstrainedRegions();
        try { }
        finally {
          DbRetVal ret = dbp->QueueStat(dbp, txp, out sp, unchecked((UInt32)mode));
          Util.CheckRetVal(ret);
          value.qsStats = *sp;
          LibDb.os_ufree(null, sp);
        }
      }
      return value;
    }

    public Stats GetStats(Txn txn, StatFlags mode) {
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return GetStats(txp, mode);
        }
      }
      else
        return GetStats((DB_TXN*)null, mode);
    }

    #endregion

    #region Nested Types

    // CLS compliant wrapper for DB_QUEUE_STAT
    public struct Stats
    {
      internal DB_QUEUE_STAT qsStats;

      /* Magic number. */
      public int Magic {
        get { return unchecked((int)qsStats.qs_magic); }
      }

      /* Version number. */
      public int Version {
        get { return unchecked((int)qsStats.qs_version); }
      }

      /* Metadata flags. */
      public int MetaFlags {
        get { return unchecked((int)qsStats.qs_metaflags); }
      }

      /* Number of unique keys. */
      public int NumKeys {
        get { return unchecked((int)qsStats.qs_nkeys); }
      }

      /* Number of data items. */
      public int NumData {
        get { return unchecked((int)qsStats.qs_ndata); }
      }

      /* Page size. */
      public int PageSize {
        get { return unchecked((int)qsStats.qs_pagesize); }
      }

      /* Pages per extent. */
      public int ExtentSize {
        get { return unchecked((int)qsStats.qs_extentsize); }
      }

      /* Data pages. */
      public int Pages {
        get { return unchecked((int)qsStats.qs_pages); }
      }

      /* Fixed-length record length. */
      public int RecordLength {
        get { return unchecked((int)qsStats.qs_re_len); }
      }

      /* Fixed-length record pad. */
      public int RecordPad {
        get { return unchecked((int)qsStats.qs_re_pad); }
      }

      /* Bytes free in data pages. */
      public int PagesFree {
        get { return unchecked((int)qsStats.qs_pgfree); }
      }

      /* First not deleted record. */
      public int FirstRecno {
        get { return unchecked((int)qsStats.qs_first_recno); }
      }

      /* Next available record number. */
      public int CurrentRecno {
        get { return unchecked((int)qsStats.qs_cur_recno); }
      }
    }

    #endregion
  }

  /// <summary>Base class for Berkeley DB databases opened for the RecNo or BTree access method.</summary>
  /// <typeparam name="C">Type of <see cref="DbFileCursor"></see> subclass.</typeparam>
  /// <typeparam name="D">Type of <see cref="DbBtreeRecno{D, C}"></see> subclass.</typeparam>
  public abstract unsafe class DbBtreeRecno<D, C>: DbFile<D, C>
    where D: DbBtreeRecno<D, C>
    where C: DbFileCursor<C, D>, new()
  {
    protected DbBtreeRecno(Db handle) : base(handle) { }

    #region Public Operations & Properties

    Stats GetStats(DB_TXN* txp, StatFlags mode) {
      Stats value;
      DB_BTREE_STAT* sp;
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        RuntimeHelpers.PrepareConstrainedRegions();
        try { }
        finally {
          DbRetVal ret = dbp->BTreeStat(dbp, txp, out sp, unchecked((UInt32)mode));
          Util.CheckRetVal(ret);
          value.btStats = *sp;
          LibDb.os_ufree(null, sp);
        }
      }
      return value;
    }

    public Stats GetStats(Txn txn, StatFlags mode) {
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return GetStats(txp, mode);
        }
      }
      else
        return GetStats((DB_TXN*)null, mode);
    }

#if BDB_4_5_20

    void Compact(
      DB_TXN* txp,
      DbEntry start,
      DbEntry stop,
      ref CompactData cdata,
      uint flags, 
      out DbEntry end)
    {
      DbRetVal ret;
      end = new DbEntry();
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        fixed (byte* startP = start.Buffer, stopP = stop.Buffer) {
          fixed (DB_COMPACT* cdataPtr = &cdata.dbCompact) {
            start.dbt.data = startP + start.Start;
            stop.dbt.data = stopP + stop.Start;
            ret = dbp->Compact(dbp, txp, &start.dbt, &stop.dbt, cdataPtr, flags, out end.dbt);
          }
        }
      }
      Util.CheckRetVal(ret);
      // this will modify end.dbt.ulen to be the same as end.Size
      end.ResizeBuffer(end.Size);
      Marshal.Copy((IntPtr)end.dbt.data, end.Buffer, 0, end.Size);
    }

    public DbEntry Compact(
      Txn txn, 
      ref DbEntry start,
      ref DbEntry stop,
      ref CompactData cdata,
      CompactFlags flags)
    {
      DbEntry end;
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          Compact(txp, start, stop, ref cdata, unchecked((uint)flags), out end);
        }
      }
      else
        Compact(null, start, stop, ref cdata, unchecked((uint)flags), out end);
      return end;
    }

    void Compact(DB_TXN* txp, uint flags) {
      DbRetVal ret;
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        ret = dbp->Compact(dbp, txp, null, null, null, flags, out *((DBT*)null));
      }
      Util.CheckRetVal(ret);
    }

    public void Compact(Txn txn, CompactFlags flags) {
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          Compact(txp, unchecked((uint)flags));
        }
      }
      else
        Compact(null, unchecked((uint)flags));
    }

#endif

    #endregion

    #region Nested Types

    // CLS compliant wrapper for DB_BTREE_STAT
    public struct Stats
    {
      internal DB_BTREE_STAT btStats;

      /* Magic number. */
      public int Magic {
        get { return unchecked((int)btStats.bt_magic); }
      }

      /* Version number. */
      public int Version {
        get { return unchecked((int)btStats.bt_version); }
      }

      /* Metadata flags. */
      public int MetaFlags {
        get { return unchecked((int)btStats.bt_metaflags); }
      }

      /* Number of unique keys. */
      public int NumKeys {
        get { return unchecked((int)btStats.bt_nkeys); }
      }

      /* Number of data items. */
      public int NumData {
        get { return unchecked((int)btStats.bt_ndata); }
      }

      /* Page size. */
      public int PageSize {
        get { return unchecked((int)btStats.bt_pagesize); }
      }

#if BDB_4_3_29
      /* Maxkey value. */
      public int MaxKey {
        get { return unchecked((int)btStats.bt_maxkey); }
      }
#endif

      /* Minkey value. */
      public int MinKey {
        get { return unchecked((int)btStats.bt_minkey); }
      }

      /* Fixed-length record length. */
      public int RecordLength {
        get { return unchecked((int)btStats.bt_re_len); }
      }

      /* Fixed-length record pad. */
      public int RecordPad {
        get { return unchecked((int)btStats.bt_re_pad); }
      }

      /* Tree levels. */
      public int Levels {
        get { return unchecked((int)btStats.bt_levels); }
      }

      /* Internal pages. */
      public int InternalPages {
        get { return unchecked((int)btStats.bt_int_pg); }
      }

      /* Leaf pages. */
      public int LeafPages {
        get { return unchecked((int)btStats.bt_leaf_pg); }
      }

      /* Duplicate pages. */
      public int DuplicatePages {
        get { return unchecked((int)btStats.bt_dup_pg); }
      }

      /* Overflow pages. */
      public int OverflowPages {
        get { return unchecked((int)btStats.bt_over_pg); }
      }

      /* Empty pages. */
      public int EmptyPages {
        get { return unchecked((int)btStats.bt_empty_pg); }
      }

      /* Pages on the free list. */
      public int FreePages {
        get { return unchecked((int)btStats.bt_free); }
      }

      /* Bytes free in internal pages. */
      public int InternalPagesFree {
        get { return unchecked((int)btStats.bt_int_pgfree); }
      }

      /* Bytes free in leaf pages. */
      public int LeafPagesFree {
        get { return unchecked((int)btStats.bt_leaf_pgfree); }
      }

      /* Bytes free in duplicate pages. */
      public int DuplicatePagesFree {
        get { return unchecked((int)btStats.bt_dup_pgfree); }
      }

      /* Bytes free in overflow pages. */
      public int OverflowPagesFree {
        get { return unchecked((int)btStats.bt_over_pgfree); }
      }
    }

#if BDB_4_5_20
    public enum CompactFlags: int
    {
      None = 0,
      FreeListOnly = DbConst.DB_FREELIST_ONLY,
      FreeSpace = DbConst.DB_FREE_SPACE
    }

    // CLS compliant wrapper for DB_COMPACT
    public struct CompactData
    {
      internal DB_COMPACT dbCompact;

      /* Input Parameters. */

      /* Desired fillfactor: 1-100 */
      public int FillPercent {
        get { return unchecked((int)dbCompact.compact_fillpercent); }
        set { dbCompact.compact_fillpercent = unchecked((uint)value); }
      }

      /* Lock timeout in microseconds. */
      public int Timeout {
        get { return unchecked((int)dbCompact.compact_timeout); }
        set { dbCompact.compact_timeout = unchecked((uint)value); }
      }

      /* Max pages to process. */
      public int Pages {
        get { return unchecked((int)dbCompact.compact_pages); }
        set { dbCompact.compact_pages = unchecked((uint)value); }
      }

      /* Output Stats. */

      /* Number of pages freed. */
      public int PagesFreed {
        get { return unchecked((int)dbCompact.compact_pages_free); }
      }

      /* Number of pages examine. */
      public int PagesExamined {
        get { return unchecked((int)dbCompact.compact_pages_examine); }
      }

      /* Number of levels removed. */
      public int LevelsRemoved {
        get { return unchecked((int)dbCompact.compact_levels); }
      }

      /* Number of deadlocks. */
      public int Deadlocks {
        get { return unchecked((int)dbCompact.compact_deadlock); }
      }

      /* Pages returned to OS. */
      public int PagesTruncated {
        get { return unchecked((int)dbCompact.compact_pages_truncated); }
      }
    }
#endif

    #endregion
  }

  /// <summary>Berkeley DB database opened for the RecNo access method.</summary>
  public unsafe class DbRecNo: DbBtreeRecno<DbRecNo, DbRecNoCursor>
  {
    static DbRecNo() {
      type = DbType.Recno;
    }

    internal DbRecNo(Db db): base(db) { }

    #region Public Operations & Properties

    public WriteStatus Append(Txn txn, ref DbEntry key, ref DbEntry data, WriteFlags wflags) {
      uint flags = DbConst.DB_APPEND | unchecked((uint)wflags);
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return Put(txp, ref key, ref data, flags);
        }
      }
      else
        return Put((DB_TXN*)null, ref key, ref data, flags);
    }

    public WriteStatus Append(Txn txn, ref DbEntry key, ref DbEntry data) {
      return Append(txn, ref key, ref data, WriteFlags.None);
    }

    #endregion
  }

  /// <summary>Berkeley DB database opened for the BTree access method.</summary>
  public unsafe class DbBTree: DbBtreeRecno<DbBTree, DbBTreeCursor>
  {
    static DbBTree() {
      type = DbType.BTree;
    }

    internal DbBTree(Db db): base(db) { }

    #region Public Operations & Properties

    // key.Data must contain a record number (UInt32) on input
    public ReadStatus GetAtRecNo(Txn txn, ref DbEntry key, ref DbEntry data, ReadFlags rflags) {
      uint flags = DbConst.DB_SET_RECNO | unchecked((uint)rflags);
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return Get(txp, ref key, ref data, flags);
        }
      }
      else
        return Get((DB_TXN*)null, ref key, ref data, flags);
    }

    // does not overwrite entry if key+data already exist
    public WriteStatus PutUnique(Txn txn, ref DbEntry key, ref DbEntry data, WriteFlags wflags) {
      uint flags = DbConst.DB_NODUPDATA | unchecked((uint)wflags);
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return Put(txp, ref key, ref data, flags);
        }
      }
      else
        return Put((DB_TXN*)null, ref key, ref data, flags);
    }

    public WriteStatus PutUnique(Txn txn, ref DbEntry key, ref DbEntry data) {
      return PutUnique(txn, ref key, ref data, WriteFlags.None);
    }

    KeyRange GetKeyRange(DB_TXN* txp, ref DbEntry key) {
      KeyRange value;
      DbRetVal ret;
      lock (db.rscLock) {
        DB* dbp = CheckDisposed();
        fixed (byte* keyBufP = key.Buffer) {
          key.dbt.data = keyBufP + key.Start;
          ret = DbKeyRange(dbp, txp, ref key.dbt, out value.keyRange, 0);
        }
      }
      Util.CheckRetVal(ret);
      return value;
    }

    public KeyRange GetKeyRange(Txn txn, ref DbEntry key) {
      if (txn != null) {
        lock (txn.rscLock) {
          DB_TXN* txp = txn.CheckDisposed();
          return GetKeyRange(txp, ref key);
        }
      }
      else
        return GetKeyRange((DB_TXN*)null, ref key);
    }

    #endregion

    #region Nested Types

    // wrapper for DB_KEY_RANGE - just for naming convention purposes
    public struct KeyRange
    {
      internal DB_KEY_RANGE keyRange;

      public double Less {
        get { return keyRange.less; }
      }

      public double Equal {
        get { return keyRange.equal; }
      }

      public double Greater {
        get { return keyRange.greater; }
      }
    }

    #endregion
  }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.