Skip to content

Instantly share code, notes, and snippets.

@wi7a1ian
Last active August 1, 2019 14:05
Show Gist options
  • Save wi7a1ian/57465a0796e55a04f86307e670a34cac to your computer and use it in GitHub Desktop.
Save wi7a1ian/57465a0796e55a04f86307e670a34cac to your computer and use it in GitHub Desktop.
Example of fine interoop between .NET Core & C compatible ABI #csharp #cpp #rust
internal static class NativeMethods
{
static class LastResult
{
public static (CDllResult, string) GetLastResult()
{
return FillLastResult(new Span<byte>(new byte[1024]));
}
private static unsafe (CDllResult, string) FillLastResult(Span<byte> buffer)
{
fixed (byte* messageBufPtr = buffer)
{
var result = c_last_result(
(IntPtr) messageBufPtr,
(UIntPtr) buffer.Length,
out var actualMessageLen,
out var lastResult);
if (result.IsBufferTooSmall()) return FillLastResult(new Span<byte>(new byte[(int) actualMessageLen]));
return (lastResult, Encoding.UTF8.GetString(messageBufPtr, (int) actualMessageLen));
}
}
}
}
internal static class NativeMethods
{
[StructLayout(LayoutKind.Sequential)]
public struct CDllResult
{
private enum Kind : uint
{
Ok,
Done,
BufferTooSmall,
ArgumentNull,
InternalError
}
private readonly Kind _result;
private readonly uint _id;
public static (CDllResult, string) GetLastResult()
{
return LastResult.GetLastResult();
}
internal CDllResult Check()
{
if (IsSuccess() || IsBufferTooSmall()) return this;
var (lastResult, msg) = GetLastResult();
// Check whether the last result kind and id are the same
// We need to use both because successful results won't
// bother setting the id (it avoids some synchronization)
if (lastResult._result == _result && lastResult._id == _id)
throw new Exception($"Native call failed ({_result}), {msg?.TrimEnd()}");
throw new Exception($"Native call failed with {_result}");
}
public bool IsSuccess()
{
return _result == Kind.Ok || _result == Kind.Done;
}
public bool IsDone()
{
return _result == Kind.Done;
}
public bool IsBufferTooSmall()
{
return _result == Kind.BufferTooSmall;
}
}
}
public sealed class Writer : IDisposable
{
private readonly WriterHandle _handle;
internal Writer(WriterHandle handle)
{
_handle = handle ?? throw new ArgumentNullException(nameof(handle));
}
public void Dispose()
{
_handle.Dispose();
}
public void Set(Key key, ReadOnlySpan<byte> value)
{
unsafe
{
var rawKey = key.Value;
var keyPtr = Unsafe.AsPointer(ref rawKey);
fixed (byte* valuePtr = value)
{
c_write_set(_handle, (IntPtr) keyPtr, (IntPtr) valuePtr, (UIntPtr) value.Length);
}
}
}
}
class WriterHandle : SafeHandle
{
private WriterHandle()
: base(IntPtr.Zero, true)
{
}
public override bool IsInvalid => handle == IntPtr.Zero;
protected override bool ReleaseHandle()
{
if (handle == IntPtr.Zero) return true;
var h = handle;
handle = IntPtr.Zero;
c_write_end(h, false);
return true;
}
}
internal static class NativeMethods
{
[DllImport(NativeLibrary, EntryPoint = "c_write_begin", ExactSpelling = true,
CallingConvention = CallingConvention.Cdecl)]
private static extern CDllResult _c_write_begin(StoreHandle store, out WriterHandle writer);
public static CDllResult c_write_begin(StoreHandle store, out WriterHandle writer, bool check = true)
{
return MaybeCheck(_c_write_begin(store, out writer), check);
}
private static CDllResult MaybeCheck(DbResult result, bool check)
{
return check ? result.Check() : result;
}
}
public Writer BeginWrite()
{
c_write_begin(out var writerHandle);
return new Writer(writerHandle);
}
@wi7a1ian
Copy link
Author

wi7a1ian commented Aug 1, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment