diff --git a/CHANGELOG.md b/CHANGELOG.md index 29813e9fcb..2fe161088d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ ### Internal - Added C# bindings for C Event Tracing API. [#1440](https://github.com/spatialos/gdk-for-unity/pull/1440) +- Added native classes for IO operations in Event Tracing API. [#1444](https://github.com/spatialos/gdk-for-unity/pull/1444) ## `0.3.9` - 2020-07-24 diff --git a/workers/unity/Packages/io.improbable.worker.sdk/CEventTrace.cs b/workers/unity/Packages/io.improbable.worker.sdk/CEventTrace.cs index 337d7e32f3..b35e12f852 100644 --- a/workers/unity/Packages/io.improbable.worker.sdk/CEventTrace.cs +++ b/workers/unity/Packages/io.improbable.worker.sdk/CEventTrace.cs @@ -278,7 +278,7 @@ public struct EventTracerParameters */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Trace_Item_Create")] - public static extern Item* ItemCreate(CIO.Storage storage, Item* item); + public static extern Item* ItemCreate(CIO.StorageHandle storage, Item* item); /** * Returns a pointer to a thread-local trace item. @@ -320,7 +320,7 @@ public struct EventTracerParameters */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Trace_SerializeItemToStream")] - public static extern Int8 SerializeItemToStream(CIO.Stream stream, Item* item, Uint32 size); + public static extern Int8 SerializeItemToStream(CIO.StreamHandle stream, Item* item, Uint32 size); /** * Get the serialized size, in bytes, of the next serialized trace item to be read from the stream. @@ -332,7 +332,7 @@ public struct EventTracerParameters */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Trace_GetNextSerializedItemSize")] - public static extern Uint32 GetNextSerializedItemSize(CIO.Stream stream); + public static extern Uint32 GetNextSerializedItemSize(CIO.StreamHandle stream); /** * Deserialize the next trace item from the stream. @@ -356,7 +356,7 @@ public struct EventTracerParameters */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Trace_DeserializeItemFromStream")] - public static extern Int8 DeserializeItemFromStream(CIO.Stream stream, Item* item, Uint32 size); + public static extern Int8 DeserializeItemFromStream(CIO.StreamHandle stream, Item* item, Uint32 size); /** * Returns the last error which occurred during a trace API method call. Returns nullptr if no diff --git a/workers/unity/Packages/io.improbable.worker.sdk/CIO.cs b/workers/unity/Packages/io.improbable.worker.sdk/CIO.cs index dfabb649d9..2bc19c2605 100644 --- a/workers/unity/Packages/io.improbable.worker.sdk/CIO.cs +++ b/workers/unity/Packages/io.improbable.worker.sdk/CIO.cs @@ -10,7 +10,7 @@ namespace Improbable.Worker.CInterop.Internal { internal unsafe class CIO { - public class Storage : CptrHandle + public class StorageHandle : CptrHandle { protected override bool ReleaseHandle() { @@ -19,7 +19,7 @@ protected override bool ReleaseHandle() } } - public class Stream : CptrHandle + public class StreamHandle : CptrHandle { protected override bool ReleaseHandle() { @@ -45,7 +45,7 @@ public enum OpenMode */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Io_Storage_Create")] - public static extern Storage StorageCreate(); + public static extern StorageHandle StorageCreate(); /* Destroys the trace storage. */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, @@ -61,7 +61,7 @@ public enum OpenMode */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Io_Storage_Clear")] - public static extern void StorageClear(IntPtr storage); + public static extern void StorageClear(StorageHandle storage); /** * Creates an I/O stream implemented as a ring buffer. @@ -77,7 +77,7 @@ public enum OpenMode */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Io_CreateRingBufferStream")] - public static extern Stream CreateRingBufferStream(Uint32 capacityBytes); + public static extern StreamHandle CreateRingBufferStream(Uint32 capacityBytes); /** * Creates an I/O stream implemented as a read/write file. @@ -94,7 +94,7 @@ public enum OpenMode */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Io_CreateFileStream")] - public static extern Stream CreateFileStream(Char* filename, OpenMode openMode); + public static extern StreamHandle CreateFileStream(Char* filename, OpenMode openMode); /* Destroys the I/O stream. */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, @@ -111,7 +111,7 @@ public enum OpenMode */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Io_Stream_Write")] - public static extern Int64 StreamWrite(Stream stream, Uint8* bytes, Uint32 length); + public static extern Int64 StreamWrite(StreamHandle stream, Uint8* bytes, Uint32 length); /** * Gets the remaining write capacity in bytes. @@ -121,7 +121,7 @@ public enum OpenMode */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Io_Stream_GetRemainingWriteCapacityBytes")] - public static extern Uint32 StreamGetRemainingWriteCapacityBytes(Stream stream); + public static extern Uint32 StreamGetRemainingWriteCapacityBytes(StreamHandle stream); /** * Reads as much of the stream's data as possible into the given buffer. @@ -133,7 +133,7 @@ public enum OpenMode */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Io_Stream_Read")] - public static extern Uint64 StreamRead(Stream stream, Uint8* bytes, Uint32 length); + public static extern Int64 StreamRead(StreamHandle stream, Uint8* bytes, Uint32 length); /** * Reads as much of the stream's data as possible into the given buffer without advancing the read @@ -142,11 +142,11 @@ public enum OpenMode * Returns the actual number of bytes read. This may be less than the given length iff the stream * has less data than the requested amount. * - * Returns -1 on error. Call StreamGetLastError() to get the associated error message. + * Returns -1 on error. Call StreamGetLastError to get the associated error message. */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Io_Stream_Peek")] - public static extern Int64 StreamPeek(Stream stream, Uint8* bytes, Uint32 length); + public static extern Int64 StreamPeek(StreamHandle stream, Uint8* bytes, Uint32 length); /** * Extracts the given number of bytes from the stream and discards them. @@ -155,11 +155,11 @@ public enum OpenMode * has advanced. This may be less than the given length iff the stream has less data than the * requested amount. * - * Returns -1 on error. Call StreamGetLastError() to get the associated error message. + * Returns -1 on error. Call StreamGetLastError to get the associated error message. */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Io_Stream_Ignore")] - public static extern Int64 StreamIgnore(Stream stream, Uint32 length); + public static extern Int64 StreamIgnore(StreamHandle stream, Uint32 length); /** * Returns the last error which occurred during an API call on this stream. Returns nullptr if no @@ -167,6 +167,6 @@ public enum OpenMode */ [DllImport(Constants.WorkerLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Io_Stream_GetLastError")] - public static extern Char* StreamGetLastError(Stream stream); + public static extern Char* StreamGetLastError(StreamHandle stream); } } diff --git a/workers/unity/Packages/io.improbable.worker.sdk/IOStorage.cs b/workers/unity/Packages/io.improbable.worker.sdk/IOStorage.cs new file mode 100644 index 0000000000..e2f58019f0 --- /dev/null +++ b/workers/unity/Packages/io.improbable.worker.sdk/IOStorage.cs @@ -0,0 +1,26 @@ +using System; +using Improbable.Worker.CInterop.Internal; + +namespace Improbable.Worker.CInterop +{ + public sealed class IOStorage : IDisposable + { + private readonly CIO.StorageHandle storage; + + public IOStorage() + { + storage = CIO.StorageCreate(); + } + + /// + public void Dispose() + { + storage.Dispose(); + } + + public void Clear() + { + CIO.StorageClear(storage); + } + } +} diff --git a/workers/unity/Packages/io.improbable.worker.sdk/IOStorage.cs.meta b/workers/unity/Packages/io.improbable.worker.sdk/IOStorage.cs.meta new file mode 100644 index 0000000000..e9e14df0a1 --- /dev/null +++ b/workers/unity/Packages/io.improbable.worker.sdk/IOStorage.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e50ee44109544e7d8d9d06fe8c1243f2 +timeCreated: 1595864556 \ No newline at end of file diff --git a/workers/unity/Packages/io.improbable.worker.sdk/IOStream.cs b/workers/unity/Packages/io.improbable.worker.sdk/IOStream.cs new file mode 100644 index 0000000000..5a48eada96 --- /dev/null +++ b/workers/unity/Packages/io.improbable.worker.sdk/IOStream.cs @@ -0,0 +1,156 @@ +using System; +using System.IO; +using Improbable.Worker.CInterop.Internal; + +namespace Improbable.Worker.CInterop +{ + public enum OpenMode + { + /* Opens the stream in the default mode. */ + OpenModeDefault = 0x00, + } + + public sealed unsafe class IOStream : IDisposable + { + private readonly CIO.StreamHandle stream; + + private IOStream(CIO.StreamHandle stream) + { + this.stream = stream; + } + + /// + public void Dispose() + { + stream.Dispose(); + } + + public static IOStream CreateRingBufferStream(uint capacity) + { + return new IOStream(CIO.CreateRingBufferStream(capacity)); + } + + public static IOStream CreateFileStream(string fileName, OpenMode openMode) + { + fixed (byte* fileNameBytes = ApiInterop.ToUtf8Cstr(fileName)) + { + return new IOStream(CIO.CreateFileStream(fileNameBytes, (CIO.OpenMode) openMode)); + } + } + + public long Write(byte[] data) + { + ThrowIfStreamClosed(); + + var remainingCapacity = CIO.StreamGetRemainingWriteCapacityBytes(stream); + if (remainingCapacity < data.Length) + { + throw new NotSupportedException("Not enough stream capacity to write data."); + } + + var bytesWritten = 0L; + fixed (byte* dataToWrite = data) + { + bytesWritten = CIO.StreamWrite(stream, dataToWrite, 1); + } + + if (bytesWritten != -1) + { + return bytesWritten; + } + + var rawError = CIO.StreamGetLastError(stream); + throw new IOException(ApiInterop.FromUtf8Cstr(rawError)); + } + + public long Read(uint bytesToRead, out byte[] streamData) + { + ThrowIfStreamClosed(); + + streamData = new byte[bytesToRead]; + + var bytesRead = 0L; + fixed (byte* streamDataPointer = streamData) + { + bytesRead = CIO.StreamRead(stream, streamDataPointer, bytesToRead); + } + + if (bytesRead != -1) + { + return bytesRead; + } + + var rawError = CIO.StreamGetLastError(stream); + throw new IOException(ApiInterop.FromUtf8Cstr(rawError)); + } + + public long Read(byte[] streamData) + { + ThrowIfStreamClosed(); + + var bytesToRead = (uint) streamData.Length; + var bytesRead = 0L; + fixed (byte* streamDataPointer = streamData) + { + bytesRead = CIO.StreamRead(stream, streamDataPointer, bytesToRead); + } + + if (bytesRead != -1) + { + return bytesRead; + } + + var rawError = CIO.StreamGetLastError(stream); + throw new IOException(ApiInterop.FromUtf8Cstr(rawError)); + } + + public long Peek(uint bytesToPeek, out byte[] streamData) + { + ThrowIfStreamClosed(); + + streamData = new byte[bytesToPeek]; + + var bytesPeeked = 0L; + fixed (byte* streamDataPointer = streamData) + { + bytesPeeked = CIO.StreamPeek(stream, streamDataPointer, bytesToPeek); + } + + if (bytesPeeked != -1) + { + return bytesPeeked; + } + + var rawError = CIO.StreamGetLastError(stream); + throw new IOException(ApiInterop.FromUtf8Cstr(rawError)); + } + + public long Ignore(uint bytesToIgnore) + { + ThrowIfStreamClosed(); + + var bytesIgnored = CIO.StreamIgnore(stream, bytesToIgnore); + + if (bytesIgnored != -1) + { + return bytesIgnored; + } + + var rawError = CIO.StreamGetLastError(stream); + throw new IOException(ApiInterop.FromUtf8Cstr(rawError)); + } + + public uint GetRemainingCapacity() + { + return CIO.StreamGetRemainingWriteCapacityBytes(stream); + } + + private void ThrowIfStreamClosed() + { + if (stream.IsClosed) + { + throw new ObjectDisposedException("Cannot access a disposed object."); + } + } + } +} diff --git a/workers/unity/Packages/io.improbable.worker.sdk/IOStream.cs.meta b/workers/unity/Packages/io.improbable.worker.sdk/IOStream.cs.meta new file mode 100644 index 0000000000..61e529facb --- /dev/null +++ b/workers/unity/Packages/io.improbable.worker.sdk/IOStream.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b0e0fbfec8ff4c5e99a8b505c9ccb705 +timeCreated: 1595944475 \ No newline at end of file