aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Common
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Common')
-rw-r--r--Ryujinx.Common/Extensions/BinaryReaderExtensions.cs14
-rw-r--r--Ryujinx.Common/Extensions/BinaryWriterExtensions.cs28
-rw-r--r--Ryujinx.Common/Extensions/StreamExtensions.cs138
-rw-r--r--Ryujinx.Common/Memory/MemoryStreamManager.cs99
-rw-r--r--Ryujinx.Common/Ryujinx.Common.csproj1
-rw-r--r--Ryujinx.Common/Utilities/EmbeddedResources.cs16
-rw-r--r--Ryujinx.Common/Utilities/StreamUtils.cs18
7 files changed, 286 insertions, 28 deletions
diff --git a/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs
index ea404d2a..21da6fc0 100644
--- a/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs
+++ b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs
@@ -12,19 +12,5 @@ namespace Ryujinx.Common
{
return MemoryMarshal.Cast<byte, T>(reader.ReadBytes(Unsafe.SizeOf<T>()))[0];
}
-
- public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value)
- where T : unmanaged
- {
- ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
-
- writer.Write(data);
- }
-
- public static void Write(this BinaryWriter writer, UInt128 value)
- {
- writer.Write((ulong)value);
- writer.Write((ulong)(value >> 64));
- }
}
}
diff --git a/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs b/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs
new file mode 100644
index 00000000..fddc8c1b
--- /dev/null
+++ b/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs
@@ -0,0 +1,28 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Common
+{
+ public static class BinaryWriterExtensions
+ {
+ public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value)
+ where T : unmanaged
+ {
+ ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
+
+ writer.Write(data);
+ }
+
+ public static void Write(this BinaryWriter writer, UInt128 value)
+ {
+ writer.Write((ulong)value);
+ writer.Write((ulong)(value >> 64));
+ }
+
+ public static void Write(this BinaryWriter writer, MemoryStream stream)
+ {
+ stream.CopyTo(writer.BaseStream);
+ }
+ }
+}
diff --git a/Ryujinx.Common/Extensions/StreamExtensions.cs b/Ryujinx.Common/Extensions/StreamExtensions.cs
new file mode 100644
index 00000000..f6fc870a
--- /dev/null
+++ b/Ryujinx.Common/Extensions/StreamExtensions.cs
@@ -0,0 +1,138 @@
+using System;
+using System.Buffers.Binary;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Common
+{
+ public static class StreamExtensions
+ {
+ /// <summary>
+ /// Writes a <cref="ReadOnlySpan<int>" /> to this stream.
+ ///
+ /// This default implementation converts each buffer value to a stack-allocated
+ /// byte array, then writes it to the Stream using <cref="System.Stream.Write(byte[])" />.
+ /// </summary>
+ /// <param name="stream">The stream to be written to</param>
+ /// <param name="buffer">The buffer of values to be written</param>
+ public static void Write(this Stream stream, ReadOnlySpan<int> buffer)
+ {
+ if (buffer.Length == 0)
+ {
+ return;
+ }
+
+ if (BitConverter.IsLittleEndian)
+ {
+ ReadOnlySpan<byte> byteBuffer = MemoryMarshal.Cast<int, byte>(buffer);
+ stream.Write(byteBuffer);
+ }
+ else
+ {
+ Span<byte> byteBuffer = stackalloc byte[sizeof(int)];
+
+ foreach (int value in buffer)
+ {
+ BinaryPrimitives.WriteInt32LittleEndian(byteBuffer, value);
+ stream.Write(byteBuffer);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Writes a four-byte signed integer to this stream. The current position
+ /// of the stream is advanced by four.
+ /// </summary>
+ /// <param name="stream">The stream to be written to</param>
+ /// <param name="value">The value to be written</param>
+ public static void Write(this Stream stream, int value)
+ {
+ Span<byte> buffer = stackalloc byte[sizeof(int)];
+ BinaryPrimitives.WriteInt32LittleEndian(buffer, value);
+ stream.Write(buffer);
+ }
+
+ /// <summary>
+ /// Writes an eight-byte signed integer to this stream. The current position
+ /// of the stream is advanced by eight.
+ /// </summary>
+ /// <param name="stream">The stream to be written to</param>
+ /// <param name="value">The value to be written</param>
+ public static void Write(this Stream stream, long value)
+ {
+ Span<byte> buffer = stackalloc byte[sizeof(long)];
+ BinaryPrimitives.WriteInt64LittleEndian(buffer, value);
+ stream.Write(buffer);
+ }
+
+ /// <summary>
+ // Writes a four-byte unsigned integer to this stream. The current position
+ // of the stream is advanced by four.
+ /// </summary>
+ /// <param name="stream">The stream to be written to</param>
+ /// <param name="value">The value to be written</param>
+ public static void Write(this Stream stream, uint value)
+ {
+ Span<byte> buffer = stackalloc byte[sizeof(uint)];
+ BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
+ stream.Write(buffer);
+ }
+
+ /// <summary>
+ /// Writes an eight-byte unsigned integer to this stream. The current
+ /// position of the stream is advanced by eight.
+ /// </summary>
+ /// <param name="stream">The stream to be written to</param>
+ /// <param name="value">The value to be written</param>
+ public static void Write(this Stream stream, ulong value)
+ {
+ Span<byte> buffer = stackalloc byte[sizeof(ulong)];
+ BinaryPrimitives.WriteUInt64LittleEndian(buffer, value);
+ stream.Write(buffer);
+ }
+
+ /// <summary>
+ /// Writes the contents of source to stream by calling source.CopyTo(stream).
+ /// Provides consistency with other Stream.Write methods.
+ /// </summary>
+ /// <param name="stream">The stream to be written to</param>
+ /// <param name="source">The stream to be read from</param>
+ public static void Write(this Stream stream, Stream source)
+ {
+ source.CopyTo(stream);
+ }
+
+ /// <summary>
+ /// Writes a sequence of bytes to the Stream.
+ /// </summary>
+ /// <param name="stream">The stream to be written to.</param>
+ /// <param name="value">The byte to be written</param>
+ /// <param name="count">The number of times the value should be written</param>
+ public static void WriteByte(this Stream stream, byte value, int count)
+ {
+ if (count <= 0)
+ {
+ return;
+ }
+
+ const int BlockSize = 16;
+
+ int blockCount = count / BlockSize;
+ if (blockCount > 0)
+ {
+ Span<byte> span = stackalloc byte[BlockSize];
+ span.Fill(value);
+ for (int x = 0; x < blockCount; x++)
+ {
+ stream.Write(span);
+ }
+ }
+
+ int nonBlockBytes = count % BlockSize;
+ for (int x = 0; x < nonBlockBytes; x++)
+ {
+ stream.WriteByte(value);
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Common/Memory/MemoryStreamManager.cs b/Ryujinx.Common/Memory/MemoryStreamManager.cs
new file mode 100644
index 00000000..68b82999
--- /dev/null
+++ b/Ryujinx.Common/Memory/MemoryStreamManager.cs
@@ -0,0 +1,99 @@
+using Microsoft.IO;
+using System;
+
+namespace Ryujinx.Common.Memory
+{
+ public static class MemoryStreamManager
+ {
+ private static readonly RecyclableMemoryStreamManager _shared = new RecyclableMemoryStreamManager();
+
+ /// <summary>
+ /// We don't expose the <c>RecyclableMemoryStreamManager</c> directly because version 2.x
+ /// returns them as <c>MemoryStream</c>. This Shared class is here to a) offer only the GetStream() versions we use
+ /// and b) return them as <c>RecyclableMemoryStream</c> so we don't have to cast.
+ /// </summary>
+ public static class Shared
+ {
+ /// <summary>
+ /// Retrieve a new <c>MemoryStream</c> object with no tag and a default initial capacity.
+ /// </summary>
+ /// <returns>A <c>RecyclableMemoryStream</c></returns>
+ public static RecyclableMemoryStream GetStream()
+ => new RecyclableMemoryStream(_shared);
+
+ /// <summary>
+ /// Retrieve a new <c>MemoryStream</c> object with the contents copied from the provided
+ /// buffer. The provided buffer is not wrapped or used after construction.
+ /// </summary>
+ /// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>
+ /// <param name="buffer">The byte buffer to copy data from</param>
+ /// <returns>A <c>RecyclableMemoryStream</c></returns>
+ public static RecyclableMemoryStream GetStream(byte[] buffer)
+ => GetStream(Guid.NewGuid(), null, buffer, 0, buffer.Length);
+
+ /// <summary>
+ /// Retrieve a new <c>MemoryStream</c> object with the given tag and with contents copied from the provided
+ /// buffer. The provided buffer is not wrapped or used after construction.
+ /// </summary>
+ /// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>
+ /// <param name="buffer">The byte buffer to copy data from</param>
+ /// <returns>A <c>RecyclableMemoryStream</c></returns>
+ public static RecyclableMemoryStream GetStream(ReadOnlySpan<byte> buffer)
+ => GetStream(Guid.NewGuid(), null, buffer);
+
+ /// <summary>
+ /// Retrieve a new <c>RecyclableMemoryStream</c> object with the given tag and with contents copied from the provided
+ /// buffer. The provided buffer is not wrapped or used after construction.
+ /// </summary>
+ /// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>
+ /// <param name="id">A unique identifier which can be used to trace usages of the stream</param>
+ /// <param name="tag">A tag which can be used to track the source of the stream</param>
+ /// <param name="buffer">The byte buffer to copy data from</param>
+ /// <returns>A <c>RecyclableMemoryStream</c></returns>
+ public static RecyclableMemoryStream GetStream(Guid id, string tag, ReadOnlySpan<byte> buffer)
+ {
+ RecyclableMemoryStream stream = null;
+ try
+ {
+ stream = new RecyclableMemoryStream(_shared, id, tag, buffer.Length);
+ stream.Write(buffer);
+ stream.Position = 0;
+ return stream;
+ }
+ catch
+ {
+ stream?.Dispose();
+ throw;
+ }
+ }
+
+ /// <summary>
+ /// Retrieve a new <c>RecyclableMemoryStream</c> object with the given tag and with contents copied from the provided
+ /// buffer. The provided buffer is not wrapped or used after construction.
+ /// </summary>
+ /// <remarks>The new stream's position is set to the beginning of the stream when returned</remarks>
+ /// <param name="id">A unique identifier which can be used to trace usages of the stream</param>
+ /// <param name="tag">A tag which can be used to track the source of the stream</param>
+ /// <param name="buffer">The byte buffer to copy data from</param>
+ /// <param name="offset">The offset from the start of the buffer to copy from</param>
+ /// <param name="count">The number of bytes to copy from the buffer</param>
+ /// <returns>A <c>RecyclableMemoryStream</c></returns>
+ public static RecyclableMemoryStream GetStream(Guid id, string tag, byte[] buffer, int offset, int count)
+ {
+ RecyclableMemoryStream stream = null;
+ try
+ {
+ stream = new RecyclableMemoryStream(_shared, id, tag, count);
+ stream.Write(buffer, offset, count);
+ stream.Position = 0;
+ return stream;
+ }
+ catch
+ {
+ stream?.Dispose();
+ throw;
+ }
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj
index c307f524..c02b11e0 100644
--- a/Ryujinx.Common/Ryujinx.Common.csproj
+++ b/Ryujinx.Common/Ryujinx.Common.csproj
@@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
+ <PackageReference Include="Microsoft.IO.RecyclableMemoryStream" />
<PackageReference Include="MsgPack.Cli" />
<PackageReference Include="System.Management" />
</ItemGroup>
diff --git a/Ryujinx.Common/Utilities/EmbeddedResources.cs b/Ryujinx.Common/Utilities/EmbeddedResources.cs
index e7c8d7d7..22b55f16 100644
--- a/Ryujinx.Common/Utilities/EmbeddedResources.cs
+++ b/Ryujinx.Common/Utilities/EmbeddedResources.cs
@@ -1,3 +1,5 @@
+using Ryujinx.Common.Memory;
+using Ryujinx.Common.Utilities;
using System;
using System.IO;
using System.Linq;
@@ -38,12 +40,7 @@ namespace Ryujinx.Common
return null;
}
- using (var mem = new MemoryStream())
- {
- stream.CopyTo(mem);
-
- return mem.ToArray();
- }
+ return StreamUtils.StreamToBytes(stream);
}
}
@@ -56,12 +53,7 @@ namespace Ryujinx.Common
return null;
}
- using (var mem = new MemoryStream())
- {
- await stream.CopyToAsync(mem);
-
- return mem.ToArray();
- }
+ return await StreamUtils.StreamToBytesAsync(stream);
}
}
diff --git a/Ryujinx.Common/Utilities/StreamUtils.cs b/Ryujinx.Common/Utilities/StreamUtils.cs
index 9bd03ec9..da97188d 100644
--- a/Ryujinx.Common/Utilities/StreamUtils.cs
+++ b/Ryujinx.Common/Utilities/StreamUtils.cs
@@ -1,4 +1,8 @@
-using System.IO;
+using Microsoft.IO;
+using Ryujinx.Common.Memory;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
namespace Ryujinx.Common.Utilities
{
@@ -6,12 +10,22 @@ namespace Ryujinx.Common.Utilities
{
public static byte[] StreamToBytes(Stream input)
{
- using (MemoryStream stream = new MemoryStream())
+ using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
{
input.CopyTo(stream);
return stream.ToArray();
}
}
+
+ public static async Task<byte[]> StreamToBytesAsync(Stream input, CancellationToken cancellationToken = default)
+ {
+ using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
+ {
+ await input.CopyToAsync(stream, cancellationToken);
+
+ return stream.ToArray();
+ }
+ }
}
} \ No newline at end of file