BinarySerializer.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. using System;
  2. using System.IO;
  3. using System.IO.Compression;
  4. using System.Runtime.CompilerServices;
  5. using System.Runtime.InteropServices;
  6. namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
  7. {
  8. /// <summary>
  9. /// Binary data serializer.
  10. /// </summary>
  11. struct BinarySerializer
  12. {
  13. private readonly Stream _stream;
  14. private Stream _activeStream;
  15. /// <summary>
  16. /// Creates a new binary serializer.
  17. /// </summary>
  18. /// <param name="stream">Stream to read from or write into</param>
  19. public BinarySerializer(Stream stream)
  20. {
  21. _stream = stream;
  22. _activeStream = stream;
  23. }
  24. /// <summary>
  25. /// Reads data from the stream.
  26. /// </summary>
  27. /// <typeparam name="T">Type of the data</typeparam>
  28. /// <param name="data">Data read</param>
  29. public void Read<T>(ref T data) where T : unmanaged
  30. {
  31. Span<byte> buffer = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref data, 1));
  32. for (int offset = 0; offset < buffer.Length;)
  33. {
  34. offset += _activeStream.Read(buffer.Slice(offset));
  35. }
  36. }
  37. /// <summary>
  38. /// Tries to read data from the stream.
  39. /// </summary>
  40. /// <typeparam name="T">Type of the data</typeparam>
  41. /// <param name="data">Data read</param>
  42. /// <returns>True if the read was successful, false otherwise</returns>
  43. public bool TryRead<T>(ref T data) where T : unmanaged
  44. {
  45. // Length is unknown on compressed streams.
  46. if (_activeStream == _stream)
  47. {
  48. int size = Unsafe.SizeOf<T>();
  49. if (_activeStream.Length - _activeStream.Position < size)
  50. {
  51. return false;
  52. }
  53. }
  54. Read(ref data);
  55. return true;
  56. }
  57. /// <summary>
  58. /// Reads data prefixed with a magic and size from the stream.
  59. /// </summary>
  60. /// <typeparam name="T">Type of the data</typeparam>
  61. /// <param name="data">Data read</param>
  62. /// <param name="magic">Expected magic value, for validation</param>
  63. public void ReadWithMagicAndSize<T>(ref T data, uint magic) where T : unmanaged
  64. {
  65. uint actualMagic = 0;
  66. int size = 0;
  67. Read(ref actualMagic);
  68. Read(ref size);
  69. if (actualMagic != magic)
  70. {
  71. throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedInvalidMagic);
  72. }
  73. // Structs are expected to expand but not shrink between versions.
  74. if (size > Unsafe.SizeOf<T>())
  75. {
  76. throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedInvalidLength);
  77. }
  78. Span<byte> buffer = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref data, 1)).Slice(0, size);
  79. for (int offset = 0; offset < buffer.Length;)
  80. {
  81. offset += _activeStream.Read(buffer.Slice(offset));
  82. }
  83. }
  84. /// <summary>
  85. /// Writes data into the stream.
  86. /// </summary>
  87. /// <typeparam name="T">Type of the data</typeparam>
  88. /// <param name="data">Data to be written</param>
  89. public void Write<T>(ref T data) where T : unmanaged
  90. {
  91. Span<byte> buffer = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref data, 1));
  92. _activeStream.Write(buffer);
  93. }
  94. /// <summary>
  95. /// Writes data prefixed with a magic and size into the stream.
  96. /// </summary>
  97. /// <typeparam name="T">Type of the data</typeparam>
  98. /// <param name="data">Data to write</param>
  99. /// <param name="magic">Magic value to write</param>
  100. public void WriteWithMagicAndSize<T>(ref T data, uint magic) where T : unmanaged
  101. {
  102. int size = Unsafe.SizeOf<T>();
  103. Write(ref magic);
  104. Write(ref size);
  105. Span<byte> buffer = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref data, 1));
  106. _activeStream.Write(buffer);
  107. }
  108. /// <summary>
  109. /// Indicates that all data that will be read from the stream has been compressed.
  110. /// </summary>
  111. public void BeginCompression()
  112. {
  113. CompressionAlgorithm algorithm = CompressionAlgorithm.None;
  114. Read(ref algorithm);
  115. if (algorithm == CompressionAlgorithm.Deflate)
  116. {
  117. _activeStream = new DeflateStream(_stream, CompressionMode.Decompress, true);
  118. }
  119. }
  120. /// <summary>
  121. /// Indicates that all data that will be written into the stream should be compressed.
  122. /// </summary>
  123. /// <param name="algorithm">Compression algorithm that should be used</param>
  124. public void BeginCompression(CompressionAlgorithm algorithm)
  125. {
  126. Write(ref algorithm);
  127. if (algorithm == CompressionAlgorithm.Deflate)
  128. {
  129. _activeStream = new DeflateStream(_stream, CompressionLevel.SmallestSize, true);
  130. }
  131. }
  132. /// <summary>
  133. /// Indicates the end of a compressed chunck.
  134. /// </summary>
  135. /// <remarks>
  136. /// Any data written after this will not be compressed unless <see cref="BeginCompression(CompressionAlgorithm)"/> is called again.
  137. /// Any data read after this will be assumed to be uncompressed unless <see cref="BeginCompression"/> is called again.
  138. /// </remarks>
  139. public void EndCompression()
  140. {
  141. if (_activeStream != _stream)
  142. {
  143. _activeStream.Dispose();
  144. _activeStream = _stream;
  145. }
  146. }
  147. /// <summary>
  148. /// Reads compressed data from the stream.
  149. /// </summary>
  150. /// <remarks>
  151. /// <paramref name="data"/> must have the exact length of the uncompressed data,
  152. /// otherwise decompression will fail.
  153. /// </remarks>
  154. /// <param name="stream">Stream to read from</param>
  155. /// <param name="data">Buffer to write the uncompressed data into</param>
  156. public static void ReadCompressed(Stream stream, Span<byte> data)
  157. {
  158. CompressionAlgorithm algorithm = (CompressionAlgorithm)stream.ReadByte();
  159. switch (algorithm)
  160. {
  161. case CompressionAlgorithm.None:
  162. stream.Read(data);
  163. break;
  164. case CompressionAlgorithm.Deflate:
  165. stream = new DeflateStream(stream, CompressionMode.Decompress, true);
  166. for (int offset = 0; offset < data.Length;)
  167. {
  168. offset += stream.Read(data.Slice(offset));
  169. }
  170. stream.Dispose();
  171. break;
  172. }
  173. }
  174. /// <summary>
  175. /// Compresses and writes the compressed data into the stream.
  176. /// </summary>
  177. /// <param name="stream">Stream to write into</param>
  178. /// <param name="data">Data to compress</param>
  179. /// <param name="algorithm">Compression algorithm to be used</param>
  180. public static void WriteCompressed(Stream stream, ReadOnlySpan<byte> data, CompressionAlgorithm algorithm)
  181. {
  182. stream.WriteByte((byte)algorithm);
  183. switch (algorithm)
  184. {
  185. case CompressionAlgorithm.None:
  186. stream.Write(data);
  187. break;
  188. case CompressionAlgorithm.Deflate:
  189. stream = new DeflateStream(stream, CompressionLevel.SmallestSize, true);
  190. stream.Write(data);
  191. stream.Dispose();
  192. break;
  193. }
  194. }
  195. }
  196. }