aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs')
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs216
1 files changed, 216 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs
new file mode 100644
index 00000000..50e37033
--- /dev/null
+++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs
@@ -0,0 +1,216 @@
+using System;
+using System.IO;
+using System.IO.Compression;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
+{
+ /// <summary>
+ /// Binary data serializer.
+ /// </summary>
+ struct BinarySerializer
+ {
+ private readonly Stream _stream;
+ private Stream _activeStream;
+
+ /// <summary>
+ /// Creates a new binary serializer.
+ /// </summary>
+ /// <param name="stream">Stream to read from or write into</param>
+ public BinarySerializer(Stream stream)
+ {
+ _stream = stream;
+ _activeStream = stream;
+ }
+
+ /// <summary>
+ /// Reads data from the stream.
+ /// </summary>
+ /// <typeparam name="T">Type of the data</typeparam>
+ /// <param name="data">Data read</param>
+ public void Read<T>(ref T data) where T : unmanaged
+ {
+ Span<byte> buffer = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref data, 1));
+ for (int offset = 0; offset < buffer.Length;)
+ {
+ offset += _activeStream.Read(buffer.Slice(offset));
+ }
+ }
+
+ /// <summary>
+ /// Tries to read data from the stream.
+ /// </summary>
+ /// <typeparam name="T">Type of the data</typeparam>
+ /// <param name="data">Data read</param>
+ /// <returns>True if the read was successful, false otherwise</returns>
+ public bool TryRead<T>(ref T data) where T : unmanaged
+ {
+ // Length is unknown on compressed streams.
+ if (_activeStream == _stream)
+ {
+ int size = Unsafe.SizeOf<T>();
+ if (_activeStream.Length - _activeStream.Position < size)
+ {
+ return false;
+ }
+ }
+
+ Read(ref data);
+ return true;
+ }
+
+ /// <summary>
+ /// Reads data prefixed with a magic and size from the stream.
+ /// </summary>
+ /// <typeparam name="T">Type of the data</typeparam>
+ /// <param name="data">Data read</param>
+ /// <param name="magic">Expected magic value, for validation</param>
+ public void ReadWithMagicAndSize<T>(ref T data, uint magic) where T : unmanaged
+ {
+ uint actualMagic = 0;
+ int size = 0;
+ Read(ref actualMagic);
+ Read(ref size);
+
+ if (actualMagic != magic)
+ {
+ throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedInvalidMagic);
+ }
+
+ // Structs are expected to expand but not shrink between versions.
+ if (size > Unsafe.SizeOf<T>())
+ {
+ throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedInvalidLength);
+ }
+
+ Span<byte> buffer = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref data, 1)).Slice(0, size);
+ for (int offset = 0; offset < buffer.Length;)
+ {
+ offset += _activeStream.Read(buffer.Slice(offset));
+ }
+ }
+
+ /// <summary>
+ /// Writes data into the stream.
+ /// </summary>
+ /// <typeparam name="T">Type of the data</typeparam>
+ /// <param name="data">Data to be written</param>
+ public void Write<T>(ref T data) where T : unmanaged
+ {
+ Span<byte> buffer = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref data, 1));
+ _activeStream.Write(buffer);
+ }
+
+ /// <summary>
+ /// Writes data prefixed with a magic and size into the stream.
+ /// </summary>
+ /// <typeparam name="T">Type of the data</typeparam>
+ /// <param name="data">Data to write</param>
+ /// <param name="magic">Magic value to write</param>
+ public void WriteWithMagicAndSize<T>(ref T data, uint magic) where T : unmanaged
+ {
+ int size = Unsafe.SizeOf<T>();
+ Write(ref magic);
+ Write(ref size);
+ Span<byte> buffer = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref data, 1));
+ _activeStream.Write(buffer);
+ }
+
+ /// <summary>
+ /// Indicates that all data that will be read from the stream has been compressed.
+ /// </summary>
+ public void BeginCompression()
+ {
+ CompressionAlgorithm algorithm = CompressionAlgorithm.None;
+ Read(ref algorithm);
+
+ if (algorithm == CompressionAlgorithm.Deflate)
+ {
+ _activeStream = new DeflateStream(_stream, CompressionMode.Decompress, true);
+ }
+ }
+
+ /// <summary>
+ /// Indicates that all data that will be written into the stream should be compressed.
+ /// </summary>
+ /// <param name="algorithm">Compression algorithm that should be used</param>
+ public void BeginCompression(CompressionAlgorithm algorithm)
+ {
+ Write(ref algorithm);
+
+ if (algorithm == CompressionAlgorithm.Deflate)
+ {
+ _activeStream = new DeflateStream(_stream, CompressionLevel.SmallestSize, true);
+ }
+ }
+
+ /// <summary>
+ /// Indicates the end of a compressed chunck.
+ /// </summary>
+ /// <remarks>
+ /// Any data written after this will not be compressed unless <see cref="BeginCompression(CompressionAlgorithm)"/> is called again.
+ /// Any data read after this will be assumed to be uncompressed unless <see cref="BeginCompression"/> is called again.
+ /// </remarks>
+ public void EndCompression()
+ {
+ if (_activeStream != _stream)
+ {
+ _activeStream.Dispose();
+ _activeStream = _stream;
+ }
+ }
+
+ /// <summary>
+ /// Reads compressed data from the stream.
+ /// </summary>
+ /// <remarks>
+ /// <paramref name="data"/> must have the exact length of the uncompressed data,
+ /// otherwise decompression will fail.
+ /// </remarks>
+ /// <param name="stream">Stream to read from</param>
+ /// <param name="data">Buffer to write the uncompressed data into</param>
+ public static void ReadCompressed(Stream stream, Span<byte> data)
+ {
+ CompressionAlgorithm algorithm = (CompressionAlgorithm)stream.ReadByte();
+
+ switch (algorithm)
+ {
+ case CompressionAlgorithm.None:
+ stream.Read(data);
+ break;
+ case CompressionAlgorithm.Deflate:
+ stream = new DeflateStream(stream, CompressionMode.Decompress, true);
+ for (int offset = 0; offset < data.Length;)
+ {
+ offset += stream.Read(data.Slice(offset));
+ }
+ stream.Dispose();
+ break;
+ }
+ }
+
+ /// <summary>
+ /// Compresses and writes the compressed data into the stream.
+ /// </summary>
+ /// <param name="stream">Stream to write into</param>
+ /// <param name="data">Data to compress</param>
+ /// <param name="algorithm">Compression algorithm to be used</param>
+ public static void WriteCompressed(Stream stream, ReadOnlySpan<byte> data, CompressionAlgorithm algorithm)
+ {
+ stream.WriteByte((byte)algorithm);
+
+ switch (algorithm)
+ {
+ case CompressionAlgorithm.None:
+ stream.Write(data);
+ break;
+ case CompressionAlgorithm.Deflate:
+ stream = new DeflateStream(stream, CompressionLevel.SmallestSize, true);
+ stream.Write(data);
+ stream.Dispose();
+ break;
+ }
+ }
+ }
+} \ No newline at end of file