/ src / Ryujinx.Common / Utilities / StreamUtils.cs
StreamUtils.cs
 1  using Microsoft.IO;
 2  using Ryujinx.Common.Memory;
 3  using System.IO;
 4  using System.Threading;
 5  using System.Threading.Tasks;
 6  
 7  namespace Ryujinx.Common.Utilities
 8  {
 9      public static class StreamUtils
10      {
11          public static byte[] StreamToBytes(Stream input)
12          {
13              using RecyclableMemoryStream output = StreamToRecyclableMemoryStream(input);
14  
15              return output.ToArray();
16          }
17  
18          public static MemoryOwner<byte> StreamToRentedMemory(Stream input)
19          {
20              if (input is MemoryStream inputMemoryStream)
21              {
22                  return MemoryStreamToRentedMemory(inputMemoryStream);
23              }
24              else if (input.CanSeek)
25              {
26                  long bytesExpected = input.Length;
27  
28                  MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)bytesExpected));
29  
30                  var destSpan = ownedMemory.Span;
31  
32                  int totalBytesRead = 0;
33  
34                  while (totalBytesRead < bytesExpected)
35                  {
36                      int bytesRead = input.Read(destSpan[totalBytesRead..]);
37  
38                      if (bytesRead == 0)
39                      {
40                          ownedMemory.Dispose();
41  
42                          throw new IOException($"Tried reading {bytesExpected} but the stream closed after reading {totalBytesRead}.");
43                      }
44  
45                      totalBytesRead += bytesRead;
46                  }
47  
48                  return ownedMemory;
49              }
50              else
51              {
52                  // If input is (non-seekable) then copy twice: first into a RecyclableMemoryStream, then to a rented IMemoryOwner<byte>.
53                  using RecyclableMemoryStream output = StreamToRecyclableMemoryStream(input);
54  
55                  return MemoryStreamToRentedMemory(output);
56              }
57          }
58  
59          public static async Task<byte[]> StreamToBytesAsync(Stream input, CancellationToken cancellationToken = default)
60          {
61              using MemoryStream stream = MemoryStreamManager.Shared.GetStream();
62  
63              await input.CopyToAsync(stream, cancellationToken);
64  
65              return stream.ToArray();
66          }
67  
68          private static MemoryOwner<byte> MemoryStreamToRentedMemory(MemoryStream input)
69          {
70              input.Position = 0;
71  
72              MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)input.Length));
73  
74              // Discard the return value because we assume reading a MemoryStream always succeeds completely.
75              _ = input.Read(ownedMemory.Span);
76  
77              return ownedMemory;
78          }
79  
80          private static RecyclableMemoryStream StreamToRecyclableMemoryStream(Stream input)
81          {
82              RecyclableMemoryStream stream = MemoryStreamManager.Shared.GetStream();
83  
84              input.CopyTo(stream);
85  
86              return stream;
87          }
88      }
89  }