mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-12-28 03:21:21 -08:00
156 lines
3.8 KiB
C#
156 lines
3.8 KiB
C#
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|