diff options
Diffstat (limited to 'Ryujinx.Common/SystemInterop/UnixStream.cs')
-rw-r--r-- | Ryujinx.Common/SystemInterop/UnixStream.cs | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/Ryujinx.Common/SystemInterop/UnixStream.cs b/Ryujinx.Common/SystemInterop/UnixStream.cs new file mode 100644 index 00000000..1d644997 --- /dev/null +++ b/Ryujinx.Common/SystemInterop/UnixStream.cs @@ -0,0 +1,155 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace Ryujinx.Common.SystemInterop +{ + [SupportedOSPlatform("linux")] + [SupportedOSPlatform("macos")] + public partial class UnixStream : Stream, IDisposable + { + private const int InvalidFd = -1; + + private int _fd; + + [LibraryImport("libc", SetLastError = true)] + private static partial long read(int fd, IntPtr buf, ulong count); + + [LibraryImport("libc", SetLastError = true)] + private static partial long write(int fd, IntPtr buf, ulong count); + + [LibraryImport("libc", SetLastError = true)] + private static partial int close(int fd); + + public UnixStream(int fd) + { + if (InvalidFd == fd) + { + throw new ArgumentException("Invalid file descriptor"); + } + + _fd = fd; + + CanRead = read(fd, IntPtr.Zero, 0) != -1; + CanWrite = write(fd, IntPtr.Zero, 0) != -1; + } + + ~UnixStream() + { + Close(); + } + + public override bool CanRead { get; } + public override bool CanWrite { get; } + public override bool CanSeek => false; + + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override void Flush() + { + } + + public override unsafe int Read([In, Out] byte[] buffer, int offset, int count) + { + if (offset < 0 || offset > (buffer.Length - count) || count < 0) + { + throw new ArgumentOutOfRangeException(); + } + + if (buffer.Length == 0) + { + return 0; + } + + long r = 0; + fixed (byte* buf = &buffer[offset]) + { + do + { + r = read(_fd, (IntPtr)buf, (ulong)count); + } while (ShouldRetry(r)); + } + + return (int)r; + } + + public override unsafe void Write(byte[] buffer, int offset, int count) + { + if (offset < 0 || offset > (buffer.Length - count) || count < 0) + { + throw new ArgumentOutOfRangeException(); + } + + if (buffer.Length == 0) + { + return; + } + + fixed (byte* buf = &buffer[offset]) + { + long r = 0; + do { + r = write(_fd, (IntPtr)buf, (ulong)count); + } while (ShouldRetry(r)); + } + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Close() + { + if (_fd == InvalidFd) + { + return; + } + + Flush(); + + int r; + do { + r = close(_fd); + } while (ShouldRetry(r)); + + _fd = InvalidFd; + } + + void IDisposable.Dispose() + { + Close(); + } + + private bool ShouldRetry(long r) + { + if (r == -1) + { + const int eintr = 4; + + int errno = Marshal.GetLastPInvokeError(); + + if (errno == eintr) + { + return true; + } + + throw new SystemException($"Operation failed with error 0x{errno:X}"); + } + + return false; + } + } +} |