📄 fastdbfields.cs
字号:
using System;
using System.Text;
using System.Collections;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace FastDbNet
{
//-------------------------------------------------------------------------
/// <summary>
/// FastDbBuffer encapsulates a binding between a memory buffer and an
/// object used to get/set data in the buffer.
/// </summary>
public class FastDbBuffer: IDisposable {
public static readonly int MIN_CAPACITY = ALIGN(30);
internal static int ALIGN(int size) { return size + size % Marshal.SizeOf(typeof(Int64)); }
public IntPtr buffer;
public string name;
public int flags; // cli_field_flags
public int bound_to_statement = -1;
// Make sure the delegate stays alive while in unmanaged code
internal unsafe static CLI.CliColumnGetEx doGetColumn = new CLI.CliColumnGetEx(GetColumn);
internal unsafe static CLI.CliColumnSetEx doSetColumn = new CLI.CliColumnSetEx(SetColumn);
private bool disposed;
public unsafe FastDbBuffer(string name, CLI.FieldType var_type, CLI.FieldFlags idx_flags) {
buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(CLI.UnmanagedBuffer)));
((CLI.UnmanagedBuffer*)buffer)->fetch_data = true;
((CLI.UnmanagedBuffer*)buffer)->type = (int)var_type;
((CLI.UnmanagedBuffer*)buffer)->size = CLI.SizeOfCliType[(int)var_type];
((CLI.UnmanagedBuffer*)buffer)->capacity = 0;
((CLI.UnmanagedBuffer*)buffer)->data = Marshal.AllocCoTaskMem(MIN_CAPACITY);
this.name = name; //Marshal.StringToHGlobalAnsi(name);
this.flags = (int)idx_flags;
disposed = false;
}
// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method does not get called.
// It gives the base class the opportunity to finalize.
~FastDbBuffer() {
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
internal unsafe static IntPtr GetColumn(int var_type, IntPtr var_ptr, ref int len,
string column_name, int statement,
void* user_data)
{
CLI.UnmanagedBuffer* buffer = (CLI.UnmanagedBuffer*)user_data;
if (CLI.IsArrayType((CLI.FieldType)var_type))
len = buffer->size / CLI.SizeOfCliType[var_type - (int)CLI.FieldType.cli_array_of_oid];
else if (var_type == (int)CLI.FieldType.cli_asciiz || var_type == (int)CLI.FieldType.cli_pasciiz)
len = buffer->size-1;
else
len = buffer->size;
return buffer->data;
}
internal unsafe static IntPtr SetColumn(int var_type,
IntPtr var_ptr, int len, string column_name,
int statement, IntPtr data_ptr,
void* user_data)
{
CLI.UnmanagedBuffer* buffer = (CLI.UnmanagedBuffer*)user_data;
if (var_type == (int)CLI.FieldType.cli_asciiz || var_type == (int)CLI.FieldType.cli_pasciiz) {
SetBufferTypeAndSize(buffer, (CLI.FieldType)var_type, len, true);
return buffer->data;
}
else if (CLI.IsArrayType((CLI.FieldType)var_type)) {
if (buffer->fetch_data) {
SetBufferTypeAndSize(buffer, (CLI.FieldType)var_type,
len*CLI.SizeOfCliType[(int)var_type - (int)CLI.FieldType.cli_array_of_oid], true);
return buffer->data;
}
else
return IntPtr.Zero; // FastDB won't fetch a field if we return nil
}
else // sanity check
throw new CliError("Unsupported type: "+Enum.GetName(typeof(CLI.FieldType), buffer->type));
}
protected unsafe static void SetBufferTypeAndSize(CLI.UnmanagedBuffer* buf,
CLI.FieldType NewType, int NewSize, bool TypeCheck)
{
int n;
if (!TypeCheck || CLI.IsArrayType(NewType))
n = NewSize;
else if (NewType == CLI.FieldType.cli_asciiz || NewType == CLI.FieldType.cli_pasciiz)
n = NewSize+1;
else
n = CLI.SizeOfCliType[(int)NewType];
if (buf->capacity != 0) {
if (n > buf->capacity) {
buf->data = Marshal.ReAllocCoTaskMem(buf->data, ALIGN(n));
buf->capacity = n;
}
} else {
if (buf->size != n && n > MIN_CAPACITY)
buf->data = Marshal.ReAllocCoTaskMem(buf->data, (n > MIN_CAPACITY) ? ALIGN(n) : MIN_CAPACITY);
}
buf->size = n;
if (buf->type != (int)NewType)
buf->type = (int)NewType;
}
/// <summary>
/// Use Assign() method to copy the content of on FastDbBuffer to another.
/// </summary>
/// <param name="var">is the source FastDbBuffer to be copied from.</param>
public unsafe virtual void Assign(FastDbBuffer var) {
this.name = var.Name;
this.Capacity = var.Capacity;
this.bound_to_statement = var.StatementID;
this.FetchData = var.FetchData;
CopyBufferData(var.Type, var.Size, ((CLI.UnmanagedBuffer*)var.buffer.ToPointer())->data);
}
internal unsafe void CopyBufferData(CLI.FieldType type, int size, IntPtr data)
{
SetBufferTypeAndSize((CLI.UnmanagedBuffer*)buffer.ToPointer(), type, size, true);
Int64* pend = (Int64*)((byte*)data.ToPointer() + ALIGN(size));
Int64* pfrom = (Int64*)data.ToPointer();
Int64* pto = (Int64*)((CLI.UnmanagedBuffer*)buffer.ToPointer())->data.ToPointer();
while(pfrom < pend)
*pto++ = *pfrom++;
// for(int i=0; i < size; i++)
// *pto++ = *pfrom++;
// Note: buffers are always aligned to 8 bytes, so we can simply copy by
// iterating over 8 byte integers.
//if (type == CLI.FieldType.cli_asciiz || type == CLI.FieldType.cli_pasciiz)
// *pto = (byte)'\0';
}
/// <summary>
/// Bind() abstract method binds a FastDbBuffer to a FastDb statement obtained by calling
/// cli_statement() API function. The descendants are requiered to override this method.
/// </summary>
/// <param name="StatementID">integer value representing a FastDB statement</param>
internal unsafe virtual void Bind(int StatementID) {} // bound_to_statement = StatementID; }
/// <summary>
/// Unbind() clears the buffer binding to a statement.
/// </summary>
internal virtual unsafe void UnBind() { bound_to_statement = -1; } // Note: since 0 is a valid statement we initialize this to a negative value
/// <summary>
/// Returns the name of this field/parameter.
/// </summary>
public unsafe string Name { get { return name; } }
/// <summary>
/// Capacity controls internally allocated memory for the buffer.
/// By default minimum size of the buffer is 30 bytes, however, if you are
/// reading/writing large arrays or string values, you may consider setting
/// the Capacity field to a larger value in order to minimize memory reallocations.
/// </summary>
public unsafe int Capacity { get { return ((CLI.UnmanagedBuffer*)buffer.ToPointer())->capacity; } set { ((CLI.UnmanagedBuffer*)buffer.ToPointer())->capacity = value; } }
/// <summary>
/// Determines the size in bytes of current value in the buffer.
/// </summary>
public unsafe int Size { get { return ((CLI.UnmanagedBuffer*)buffer.ToPointer())->size; } }
/// <summary>
/// Returns the type of the value in the buffer.
/// </summary>
public unsafe CLI.FieldType Type { get { return (CLI.FieldType)((CLI.UnmanagedBuffer*)buffer.ToPointer())->type; } }
/// <summary>
/// Contains index flags indicating the types of indexes (hash/T-tree) on this field.
/// </summary>
public unsafe CLI.FieldFlags Flags { get { return (CLI.FieldFlags)flags; } }
/// <summary>
/// Internal statement's ID that this buffer is bound to.
/// </summary>
public unsafe int StatementID { get { return bound_to_statement; } }
/// <summary>
/// For array fields FetchData controls whether to copy the field's content
/// to the buffer when a cursor is moved to another record. By setting this
/// value to false, it may increase performance on queries that don't need to use
/// the content of the array field.
/// </summary>
public unsafe bool FetchData { get { return ((CLI.UnmanagedBuffer*)buffer.ToPointer())->fetch_data; } set { ((CLI.UnmanagedBuffer*)buffer.ToPointer())->fetch_data = value; } }
public bool asBoolean {get {return (bool)getValue(typeof(bool));} set {setValue(value);}}
public Int16 asInt16 {get {return (Int16)getValue(typeof(Int16));} set {setValue(value);}}
public int asInteger {get {return (int)getValue(typeof(int));} set {setValue(value);}}
public uint asOID {get {return (uint)getValue(typeof(uint));} set {setValue(value);}}
public Int64 asInt64 {get {return (Int64)getValue(typeof(Int64));} set {setValue(value);}}
public double asDouble {get {return (double)getValue(typeof(double));} set {setValue(value);}}
public string asString {get {return (string)getValue(typeof(string));} set {setValue(value);}}
protected unsafe virtual Object getValue(Type t) {
switch((CLI.FieldType)((CLI.UnmanagedBuffer*)buffer.ToPointer())->type) {
case CLI.FieldType.cli_bool:
case CLI.FieldType.cli_int1: return Convert.ChangeType(*(sbyte*)((CLI.UnmanagedBuffer*)buffer.ToPointer())->data.ToPointer(), t);
case CLI.FieldType.cli_int2: return Convert.ChangeType(*(short*)((CLI.UnmanagedBuffer*)buffer.ToPointer())->data.ToPointer(), t);
case CLI.FieldType.cli_oid: return Convert.ChangeType(*(uint*)((CLI.UnmanagedBuffer*)buffer.ToPointer())->data.ToPointer(), t);
case CLI.FieldType.cli_int4: return Convert.ChangeType(*(int*)((CLI.UnmanagedBuffer*)buffer.ToPointer())->data.ToPointer(), t);
case CLI.FieldType.cli_int8: return Convert.ChangeType(*(Int64*)((CLI.UnmanagedBuffer*)buffer.ToPointer())->data.ToPointer(), t);
case CLI.FieldType.cli_real4: return Convert.ChangeType(*(Single*)((CLI.UnmanagedBuffer*)buffer.ToPointer())->data.ToPointer(), t);
case CLI.FieldType.cli_datetime:
case CLI.FieldType.cli_real8: return Convert.ChangeType(*(double*)((CLI.UnmanagedBuffer*)buffer.ToPointer())->data.ToPointer(), t);
case CLI.FieldType.cli_asciiz:
case CLI.FieldType.cli_pasciiz: return Convert.ChangeType(Marshal.PtrToStringAnsi(((CLI.UnmanagedBuffer*)buffer.ToPointer())->data), t);
default: throw new CliError("getValue: Unsupported type!"+Enum.GetName(typeof(CLI.FieldType), ((CLI.UnmanagedBuffer*)buffer.ToPointer())->type));
}
}
protected unsafe void setValue(Object Value) {
switch((CLI.FieldType)((CLI.UnmanagedBuffer*)buffer.ToPointer())->type) {
case CLI.FieldType.cli_oid:
*(uint*)((CLI.UnmanagedBuffer*)buffer.ToPointer())->data.ToPointer() = Convert.ToUInt32(Value);
break;
case CLI.FieldType.cli_int4:
*(int*)((CLI.UnmanagedBuffer*)buffer.ToPointer())->data.ToPointer() = Convert.ToInt32(Value);
break;
case CLI.FieldType.cli_bool:
case CLI.FieldType.cli_int1:
*(sbyte*)((CLI.UnmanagedBuffer*)buffer.ToPointer())->data.ToPointer() = Convert.ToSByte(Value);
break;
case CLI.FieldType.cli_int2:
*(Int16*)((CLI.UnmanagedBuffer*)buffer.ToPointer())->data.ToPointer() = Convert.ToInt16(Value);
break;
case CLI.FieldType.cli_int8:
*(Int64*)((CLI.UnmanagedBuffer*)buffer.ToPointer())->data.ToPointer() = Convert.ToInt64(Value);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -