using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace XFESystem.Kernel;
/// <summary>
/// NativeAOT PAL scatter/gather I/O implemented through Cosmos's existing
/// plugged positional single-buffer I/O. It preserves short-read semantics.
/// </summary>
public static unsafe class VectorFileIo
{
[StructLayout(LayoutKind.Sequential)]
public struct IoVector
{
public byte* Base;
public nuint Count;
}
[UnmanagedCallersOnly(EntryPoint = "SystemNative_PReadV")]
public static long Read(nint descriptor, IoVector* vectors, int count, long offset)
=> Transfer(descriptor, vectors, count, offset, false);
[UnmanagedCallersOnly(EntryPoint = "SystemNative_PWriteV")]
public static long Write(nint descriptor, IoVector* vectors, int count, long offset)
=> Transfer(descriptor, vectors, count, offset, true);
public static long Transfer(nint descriptor, IoVector* vectors, int count, long offset, bool write)
{
if (count < 0 || count > 1024 || offset < 0 || count > 0 && vectors == null)
return Fail(0x1001C); // .NET PAL EINVAL
if (count == 0) return 0;
long total = 0;
try
{
using var handle = new SafeFileHandle(descriptor, ownsHandle: false);
for (var index = 0; index < count; index++)
{
var vector = vectors[index];
if (vector.Count > int.MaxValue || vector.Count != 0 && vector.Base == null)
return total == 0 ? Fail(0x1001C) : total;
var length = (int)vector.Count;
if (length == 0) continue;
if (offset > long.MaxValue - total || offset + total > long.MaxValue - length)
return total == 0 ? Fail(0x10040) : total; // PAL EOVERFLOW
if (write)
{
RandomAccess.Write(handle, new ReadOnlySpan<byte>(vector.Base, length), offset + total);
total += length;
}
else
{
int read = RandomAccess.Read(handle, new Span<byte>(vector.Base, length), offset + total);
total += read;
if (read < length) break;
}
}
return total;
}
catch (Exception error) when (error is IOException or ArgumentException or UnauthorizedAccessException or ObjectDisposedException or NotSupportedException)
{
int code = error is UnauthorizedAccessException ? 0x10002 : error is ObjectDisposedException ? 0x10008 :
error is NotSupportedException ? 0x1003D : error is ArgumentException ? 0x1001C : 0x1001D;
return total == 0 ? Fail(code) : total;
}
}
private static long Fail(int error)
{
Marshal.SetLastPInvokeError(error);
return -1;
}
}
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace XFESystem.Kernel;
/// <summary>
/// NativeAOT PAL scatter/gather I/O implemented through Cosmos's existing
/// plugged positional single-buffer I/O. It preserves short-read semantics.
/// </summary>
public static unsafe class VectorFileIo
{
[StructLayout(LayoutKind.Sequential)]
public struct IoVector
{
public byte* Base;
public nuint Count;
}
[UnmanagedCallersOnly(EntryPoint = "SystemNative_PReadV")]
public static long Read(nint descriptor, IoVector* vectors, int count, long offset)
=> Transfer(descriptor, vectors, count, offset, false);
[UnmanagedCallersOnly(EntryPoint = "SystemNative_PWriteV")]
public static long Write(nint descriptor, IoVector* vectors, int count, long offset)
=> Transfer(descriptor, vectors, count, offset, true);
public static long Transfer(nint descriptor, IoVector* vectors, int count, long offset, bool write)
{
if (count < 0 || count > 1024 || offset < 0 || count > 0 && vectors == null)
return Fail(0x1001C); // .NET PAL EINVAL
if (count == 0) return 0;
long total = 0;
try
{
using var handle = new SafeFileHandle(descriptor, ownsHandle: false);
for (var index = 0; index < count; index++)
{
var vector = vectors[index];
if (vector.Count > int.MaxValue || vector.Count != 0 && vector.Base == null)
return total == 0 ? Fail(0x1001C) : total;
var length = (int)vector.Count;
if (length == 0) continue;
if (offset > long.MaxValue - total || offset + total > long.MaxValue - length)
return total == 0 ? Fail(0x10040) : total; // PAL EOVERFLOW
if (write)
{
RandomAccess.Write(handle, new ReadOnlySpan<byte>(vector.Base, length), offset + total);
total += length;
}
else
{
int read = RandomAccess.Read(handle, new Span<byte>(vector.Base, length), offset + total);
total += read;
if (read < length) break;
}
}
return total;
}
catch (Exception error) when (error is IOException or ArgumentException or UnauthorizedAccessException or ObjectDisposedException or NotSupportedException)
{
int code = error is UnauthorizedAccessException ? 0x10002 : error is ObjectDisposedException ? 0x10008 :
error is NotSupportedException ? 0x1003D : error is ArgumentException ? 0x1001C : 0x1001D;
return total == 0 ? Fail(code) : total;
}
}
private static long Fail(int error)
{
Marshal.SetLastPInvokeError(error);
return -1;
}
}