diff options
Diffstat (limited to 'Ryujinx.Common')
| -rw-r--r-- | Ryujinx.Common/Extensions/BinaryReaderExtensions.cs | 14 | ||||
| -rw-r--r-- | Ryujinx.Common/Extensions/BinaryWriterExtensions.cs | 28 | ||||
| -rw-r--r-- | Ryujinx.Common/Extensions/StreamExtensions.cs | 138 | ||||
| -rw-r--r-- | Ryujinx.Common/Memory/MemoryStreamManager.cs | 99 | ||||
| -rw-r--r-- | Ryujinx.Common/Ryujinx.Common.csproj | 1 | ||||
| -rw-r--r-- | Ryujinx.Common/Utilities/EmbeddedResources.cs | 16 | ||||
| -rw-r--r-- | Ryujinx.Common/Utilities/StreamUtils.cs | 18 |
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 |
