/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2009 Oracle. All rights reserved.
*
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using BerkeleyDB.Internal;
namespace BerkeleyDB {
///
/// A class representing a key or data item in a Berkeley DB database
///
public class DatabaseEntry : IDisposable{
internal DBT dbt;
private byte[] _data;
internal static DBT getDBT(DatabaseEntry ent) {
return ent == null ? null : ent.dbt;
}
internal static DatabaseEntry fromDBT(DBT dbt) {
if (dbt.app_data != null)
return dbt.app_data;
else
return new DatabaseEntry(dbt);
}
private DatabaseEntry(DBT dbtp) {
dbt = dbtp;
Data = (byte[])dbtp.data.Clone();
}
///
/// Create a new, empty DatabaseEntry object.
///
public DatabaseEntry() {
dbt = new DBT();
if (!this.GetType().IsGenericType)
dbt.app_data = this;
}
///
/// Create a new DatabaseEntry object, with the specified data
///
/// The new object's
public DatabaseEntry(byte[] data)
: this() {
Data = data;
}
///
/// The byte string stored in or retrieved from a database
///
public byte[] Data {
/*
* Data to be stored in the DB or has been retrieved from the DB. We
* keep it in C#'s managed memory here, dbt_usercopy will copy it
* to/from the library as needed. Need to set the size of the DBT in
* the C library so that dbt_usercopy knows there's data to copy.
*/
get { return _data; }
set {
_data = value;
dbt.size = value == null ? 0 : (uint)value.Length;
}
}
internal byte[] UserData {
get { return dbt.data; }
set {
dbt.data = value;
ulen = (uint)value.Length;
flags &= ~DbConstants.DB_DBT_USERCOPY;
flags |= DbConstants.DB_DBT_USERMEM;
}
}
///
/// Release the resources held by the underlying C library.
///
public virtual void Dispose() {
dbt.Dispose();
}
/*
* Copy between the C# byte array and the C library's void * as needed.
* The library will call this method when it needs data from us or has
* data to give us. This prevents us from needing to copy all data in
* and all data out. The callback to this method gets set when the
* DatabaseEnvironment is created (or the Database if created w/o an
* environment.)
*/
internal static int dbt_usercopy(IntPtr dbtp, uint offset, IntPtr buf, uint size, uint flags) {
DBT dbt = new DBT(dbtp, false);
DatabaseEntry ent = dbt.app_data;
if (flags == DbConstants.DB_USERCOPY_GETDATA)
Marshal.Copy(ent.Data, 0, buf, (int)size);
else {
/*
* If the offset is zero, we're writing a new buffer and can
* simply allocate byte array. If the offset is not zero,
* however, we are appending to the exisiting array. Since we
* can't extend it, we have to allocate a new one and copy.
*
* Our caller is setting dbt.size, so set ent._data directly,
* since ent.Data would overwrite dbt.size.
*/
if (offset != 0) {
byte[] t = new byte[ent.Data.Length + (int)size];
ent.Data.CopyTo(t, 0);
ent._data = t;
} else
ent._data = new byte[(int)size];
Marshal.Copy(buf, ent.Data, (int)offset, (int)size);
}
return 0;
}
internal uint ulen {
get { return dbt.ulen; }
set { dbt.ulen = value; }
}
internal uint flags {
get { return dbt.flags; }
set { dbt.flags = value; }
}
internal uint size {
get { return dbt.size; }
set { dbt.size = value; }
}
}
}