diff options
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs')
-rw-r--r-- | src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BinarySerializer.cs | 216 |
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 |