| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- 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;
- }
- }
- }
- }
|