2024-04-14 16:06:14 -04:00
|
|
|
using Microsoft.IO;
|
2023-12-04 14:17:13 +01:00
|
|
|
using Ryujinx.Common.Memory;
|
2024-04-14 16:06:14 -04:00
|
|
|
using System.Buffers;
|
2023-03-17 08:14:50 -04:00
|
|
|
using System.IO;
|
|
|
|
using System.Threading;
|
|
|
|
using System.Threading.Tasks;
|
2021-09-15 01:24:49 +02:00
|
|
|
|
|
|
|
namespace Ryujinx.Common.Utilities
|
|
|
|
{
|
|
|
|
public static class StreamUtils
|
|
|
|
{
|
|
|
|
public static byte[] StreamToBytes(Stream input)
|
|
|
|
{
|
2024-04-14 16:06:14 -04:00
|
|
|
using RecyclableMemoryStream output = StreamToRecyclableMemoryStream(input);
|
2021-09-15 01:24:49 +02:00
|
|
|
|
2024-04-14 16:06:14 -04:00
|
|
|
return output.ToArray();
|
|
|
|
}
|
2023-06-28 18:41:38 +02:00
|
|
|
|
2024-04-14 16:06:14 -04:00
|
|
|
public static IMemoryOwner<byte> StreamToRentedMemory(Stream input)
|
|
|
|
{
|
|
|
|
if (input is MemoryStream inputMemoryStream)
|
|
|
|
{
|
|
|
|
return MemoryStreamToRentedMemory(inputMemoryStream);
|
|
|
|
}
|
|
|
|
else if (input.CanSeek)
|
|
|
|
{
|
|
|
|
long bytesExpected = input.Length;
|
2023-06-28 18:41:38 +02:00
|
|
|
|
2024-04-14 16:06:14 -04:00
|
|
|
IMemoryOwner<byte> ownedMemory = ByteMemoryPool.Rent(bytesExpected);
|
|
|
|
|
|
|
|
var destSpan = ownedMemory.Memory.Span;
|
|
|
|
|
|
|
|
int totalBytesRead = 0;
|
|
|
|
|
|
|
|
while (totalBytesRead < bytesExpected)
|
|
|
|
{
|
|
|
|
int bytesRead = input.Read(destSpan[totalBytesRead..]);
|
|
|
|
|
|
|
|
if (bytesRead == 0)
|
|
|
|
{
|
|
|
|
ownedMemory.Dispose();
|
|
|
|
|
|
|
|
throw new IOException($"Tried reading {bytesExpected} but the stream closed after reading {totalBytesRead}.");
|
|
|
|
}
|
|
|
|
|
|
|
|
totalBytesRead += bytesRead;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ownedMemory;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// If input is (non-seekable) then copy twice: first into a RecyclableMemoryStream, then to a rented IMemoryOwner<byte>.
|
|
|
|
using RecyclableMemoryStream output = StreamToRecyclableMemoryStream(input);
|
|
|
|
|
|
|
|
return MemoryStreamToRentedMemory(output);
|
|
|
|
}
|
2021-09-15 01:24:49 +02:00
|
|
|
}
|
2023-03-17 08:14:50 -04:00
|
|
|
|
|
|
|
public static async Task<byte[]> StreamToBytesAsync(Stream input, CancellationToken cancellationToken = default)
|
|
|
|
{
|
2023-06-28 18:41:38 +02:00
|
|
|
using MemoryStream stream = MemoryStreamManager.Shared.GetStream();
|
|
|
|
|
|
|
|
await input.CopyToAsync(stream, cancellationToken);
|
2023-03-17 08:14:50 -04:00
|
|
|
|
2023-06-28 18:41:38 +02:00
|
|
|
return stream.ToArray();
|
2023-03-17 08:14:50 -04:00
|
|
|
}
|
2024-04-14 16:06:14 -04:00
|
|
|
|
|
|
|
private static IMemoryOwner<byte> MemoryStreamToRentedMemory(MemoryStream input)
|
|
|
|
{
|
|
|
|
input.Position = 0;
|
|
|
|
|
|
|
|
IMemoryOwner<byte> ownedMemory = ByteMemoryPool.Rent(input.Length);
|
|
|
|
|
|
|
|
// Discard the return value because we assume reading a MemoryStream always succeeds completely.
|
|
|
|
_ = input.Read(ownedMemory.Memory.Span);
|
|
|
|
|
|
|
|
return ownedMemory;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static RecyclableMemoryStream StreamToRecyclableMemoryStream(Stream input)
|
|
|
|
{
|
|
|
|
RecyclableMemoryStream stream = MemoryStreamManager.Shared.GetStream();
|
|
|
|
|
|
|
|
input.CopyTo(stream);
|
|
|
|
|
|
|
|
return stream;
|
|
|
|
}
|
2021-09-15 01:24:49 +02:00
|
|
|
}
|
2023-06-28 18:41:38 +02:00
|
|
|
}
|