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