using System;
using System.Runtime.InteropServices;
namespace Xpace
{
#region external structs
#if _WIN64
[StructLayout(LayoutKind.Explicit, Size = 12)]
#else
[StructLayout(LayoutKind.Explicit, Size = 8)]
#endif
public unsafe struct Xpace_ResultPage
{
[FieldOffset(0)]
public uint end;
[FieldOffset(4)]
public byte* buf;
};
#if _WIN64
[StructLayout(LayoutKind.Explicit, Size = 20)]
#else
[StructLayout(LayoutKind.Explicit, Size = 16)]
#endif
public unsafe struct Xpace_Result
{
[FieldOffset(0)]
public int columns;
[FieldOffset(4)]
public uint docCount;
[FieldOffset(8)]
public uint pageCount;
[FieldOffset(12)]
public Xpace_ResultPage* pages;
};
#endregion
public class Result : IDisposable
{
#region DLL calls
// extern void _stdcall Xpace_Result_Destroy
// (Xpace_Result* result);
[DllImport("xpace.dll")]
private static unsafe extern void Xpace_Result_Destroy
(Xpace_Result* result);
#endregion
#region private fields
private Xpace_Result _res;
private int _cur_page_num;
private unsafe Xpace_ResultPage* _cur_page;
private int _cur_pos;
private int _cur_col;
private unsafe byte* _col_bits;
private bool _disposed = false;
#endregion
#region properties
public UInt64 Count
{
get
{
unsafe
{
return _res.docCount;
}
}
}
public unsafe byte* Get
{
get
{
return (!isDone && col_exists(_cur_col)) ? (_cur_page->buf + _cur_pos) : null;
}
}
public bool isDone
{
get
{
unsafe
{
return _cur_page == null;
}
}
}
#endregion
#region constructor / destructor
///
/// Constructor Takes ownership of res
///
/// The raw Xpace_Result
internal unsafe Result(Xpace_Result* res)
{
_res = *res;
_cur_page_num = 0;
_cur_page = &_res.pages[0];
_col_bits = _cur_page->buf;
_cur_pos = (_res.columns + 7) >> 3;
_cur_col = 0;
}
public void Dispose
()
{
CleanUp();
GC.SuppressFinalize(this);
}
private void CleanUp
()
{
if (!_disposed)
{
unsafe
{
fixed (Xpace_Result* r = &_res)
{
Xpace_Result_Destroy(r);
}
}
_disposed = false;
}
}
~Result
()
{
CleanUp();
}
#endregion
#region public functions
public void next(int size)
{
unsafe
{
if (col_exists(_cur_col))
check_page(size);
if ((++_cur_col == _res.columns) && (_cur_page != null))
{
// last column; get ready for next row
_col_bits = _cur_page->buf + _cur_pos;
if (check_page((_res.columns + 7) >> 3))
{
_col_bits = _cur_page->buf;
_cur_pos = (_res.columns + 7) >> 3;
}
_cur_col = 0;
}
}
}
#endregion
#region private functions
// return true iff we move off this page
private bool check_page(int size)
{
unsafe
{
if ((_cur_pos += size) < _cur_page->end)
return false;
if (++_cur_page_num >= _res.pageCount)
{
_cur_page = null;
return true;
}
_cur_page = &_res.pages[_cur_page_num];
_cur_pos = 0;
return true;
}
}
private bool col_exists(int col)
{
unsafe
{
return (_res.columns == 0) || Convert.ToBoolean(_col_bits[_cur_col >> 3] & (1 << (_cur_col & 7)));
}
}
#endregion
}
///
/// Reads data from a Result: definitions read data of specific sorts
///
interface IResultReader
{
bool read(ref Result res);
object Get { get; }
}
///
/// Functionality common to all Result Readers
///
public abstract class ResultReader : IResultReader
{
protected bool ok;
public bool read(ref Result res)
{
unsafe
{
IntPtr p = (IntPtr)res.Get;
if (ok = p != IntPtr.Zero)
setVal(p, ref res);
return ok;
}
}
public abstract object Get { get; }
protected abstract void setVal(IntPtr p, ref Result res);
};
///
/// Read a string from a Result
///
public class TextResultReader : ResultReader
{
protected override void setVal(IntPtr p, ref Result res)
{
val = Marshal.PtrToStringUni(p);
res.next(sizeof(char) * (val.Length + 1));
}
public override object Get
{
get { return ok ? val : null; }
}
public override string ToString()
{
return val;
}
private string val;
}
///
/// Read a UInt64 from a result
///
public class UInt64ResultReader : ResultReader
{
protected override void setVal(IntPtr p, ref Result res)
{
val = (UInt64) Marshal.ReadInt64(p);
res.next(sizeof(long));
}
public override object Get
{
get
{
if (ok)
return val;
return null;
}
}
public override string ToString()
{
return val.ToString();
}
public UInt64 getUInt64
{
get
{
if (ok)
return val;
return 0;
}
}
private UInt64 val;
}
///
/// Read an Int32 from a Result
///
public class Int32ResultReader : ResultReader
{
protected override void setVal(IntPtr p, ref Result res)
{
val = Marshal.ReadInt32(p);
res.next(sizeof(int));
}
public override object Get
{
get
{
if (ok)
return val;
return null;
}
}
public override string ToString()
{
return val.ToString();
}
public int getInt32
{
get
{
if (ok)
return val;
return 0;
}
}
private int val;
}
} // namespace Xpace