You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

382 lines
12 KiB

4 years ago
  1. #if MYSQL_6_10
  2. // Copyright � 2004, 2016, Oracle and/or its affiliates. All rights reserved.
  3. //
  4. // MySQL Connector/NET is licensed under the terms of the GPLv2
  5. // <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
  6. // MySQL Connectors. There are special exceptions to the terms and
  7. // conditions of the GPLv2 as it is applied to this software, see the
  8. // FLOSS License Exception
  9. // <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
  10. //
  11. // This program is free software; you can redistribute it and/or modify
  12. // it under the terms of the GNU General Public License as published
  13. // by the Free Software Foundation; version 2 of the License.
  14. //
  15. // This program is distributed in the hope that it will be useful, but
  16. // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  17. // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  18. // for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License along
  21. // with this program; if not, write to the Free Software Foundation, Inc.,
  22. // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  23. using System;
  24. using System.IO;
  25. using System.IO.Compression;
  26. using System.Linq;
  27. using Externals.MySql.Data.Common;
  28. using System.Net;
  29. namespace Externals.MySql.Data.MySqlClient
  30. {
  31. /// <summary>
  32. /// Summary description for CompressedStream.
  33. /// </summary>
  34. internal class CompressedStream : Stream
  35. {
  36. // writing fields
  37. private Stream baseStream;
  38. private MemoryStream cache;
  39. // reading fields
  40. private byte[] localByte;
  41. private byte[] inBuffer;
  42. private byte[] lengthBytes;
  43. private WeakReference inBufferRef;
  44. private int inPos;
  45. private int maxInPos;
  46. private DeflateStream compInStream;
  47. public CompressedStream(Stream baseStream)
  48. {
  49. this.baseStream = baseStream;
  50. localByte = new byte[1];
  51. lengthBytes = new byte[7];
  52. cache = new MemoryStream();
  53. inBufferRef = new WeakReference(inBuffer, false);
  54. }
  55. #region Properties
  56. public override bool CanRead => baseStream.CanRead;
  57. public override bool CanWrite => baseStream.CanWrite;
  58. public override bool CanSeek => baseStream.CanSeek;
  59. public override long Length => baseStream.Length;
  60. public override long Position
  61. {
  62. get { return baseStream.Position; }
  63. set { baseStream.Position = value; }
  64. }
  65. #endregion
  66. #if NETSTANDARD1_3
  67. public void Close()
  68. {
  69. base.Dispose();
  70. #else
  71. public override void Close()
  72. {
  73. base.Close();
  74. #endif
  75. #if NETSTANDARD1_3
  76. baseStream.Dispose();
  77. #else
  78. baseStream.Close();
  79. #endif
  80. cache.Dispose();
  81. }
  82. public override void SetLength(long value)
  83. {
  84. throw new NotSupportedException(Resources.CSNoSetLength);
  85. }
  86. public override int ReadByte()
  87. {
  88. try
  89. {
  90. Read(localByte, 0, 1);
  91. return localByte[0];
  92. }
  93. catch (EndOfStreamException)
  94. {
  95. return -1;
  96. }
  97. }
  98. public override bool CanTimeout => baseStream.CanTimeout;
  99. public override int ReadTimeout
  100. {
  101. get
  102. {
  103. return baseStream.ReadTimeout;
  104. }
  105. set
  106. {
  107. baseStream.ReadTimeout = value;
  108. }
  109. }
  110. public override int WriteTimeout
  111. {
  112. get
  113. {
  114. return baseStream.WriteTimeout;
  115. }
  116. set
  117. {
  118. baseStream.WriteTimeout = value;
  119. }
  120. }
  121. public override int Read(byte[] buffer, int offset, int count)
  122. {
  123. if (buffer == null)
  124. throw new ArgumentNullException(nameof(buffer), Resources.BufferCannotBeNull);
  125. if (offset < 0 || offset >= buffer.Length)
  126. throw new ArgumentOutOfRangeException(nameof(offset), Resources.OffsetMustBeValid);
  127. if ((offset + count) > buffer.Length)
  128. throw new ArgumentException(Resources.BufferNotLargeEnough, nameof(buffer));
  129. if (inPos == maxInPos)
  130. PrepareNextPacket();
  131. int countToRead = Math.Min(count, maxInPos - inPos);
  132. int countRead;
  133. if (compInStream != null)
  134. countRead = compInStream.Read(buffer, offset, countToRead);
  135. else
  136. countRead = baseStream.Read(buffer, offset, countToRead);
  137. inPos += countRead;
  138. // release the weak reference
  139. if (inPos == maxInPos)
  140. {
  141. compInStream = null;
  142. if (!Platform.IsMono())
  143. {
  144. inBufferRef = new WeakReference(inBuffer, false);
  145. inBuffer = null;
  146. }
  147. }
  148. return countRead;
  149. }
  150. private void PrepareNextPacket()
  151. {
  152. MySqlStream.ReadFully(baseStream, lengthBytes, 0, 7);
  153. int compressedLength = lengthBytes[0] + (lengthBytes[1] << 8) + (lengthBytes[2] << 16);
  154. // lengthBytes[3] is seq
  155. int unCompressedLength = lengthBytes[4] + (lengthBytes[5] << 8) +
  156. (lengthBytes[6] << 16);
  157. if (unCompressedLength == 0)
  158. {
  159. unCompressedLength = compressedLength;
  160. compInStream = null;
  161. }
  162. else
  163. {
  164. ReadNextPacket(compressedLength);
  165. MemoryStream ms = new MemoryStream(inBuffer, 2, compressedLength - 2);
  166. compInStream = new DeflateStream(ms, CompressionMode.Decompress);
  167. }
  168. inPos = 0;
  169. maxInPos = unCompressedLength;
  170. }
  171. private void ReadNextPacket(int len)
  172. {
  173. inBuffer = inBufferRef.Target as byte[];
  174. if (inBuffer == null || inBuffer.Length < len)
  175. inBuffer = new byte[len];
  176. MySqlStream.ReadFully(baseStream, inBuffer, 0, len);
  177. }
  178. private MemoryStream CompressCache()
  179. {
  180. // small arrays almost never yeild a benefit from compressing
  181. if (cache.Length < 50)
  182. return null;
  183. #if NETSTANDARD1_3
  184. byte[] cacheBytes;
  185. ArraySegment<byte> cacheBuffer;
  186. var cacheResult = cache.TryGetBuffer(out cacheBuffer);
  187. if (cacheResult)
  188. cacheBytes = cacheBuffer.ToArray();
  189. else // if the conversion fail, then just return null
  190. return null;
  191. #else
  192. byte[] cacheBytes = cache.GetBuffer();
  193. #endif
  194. MemoryStream compressedBuffer = new MemoryStream();
  195. compressedBuffer.WriteByte(0x78);
  196. compressedBuffer.WriteByte(0x9c);
  197. var outCompStream = new DeflateStream(compressedBuffer, CompressionMode.Compress, true);
  198. outCompStream.Write(cacheBytes, 0, (int)cache.Length);
  199. outCompStream.Dispose();
  200. int adler = IPAddress.HostToNetworkOrder(Adler32(cacheBytes, 0, (int)cache.Length));
  201. compressedBuffer.Write(BitConverter.GetBytes(adler), 0, sizeof(uint));
  202. // if the compression hasn't helped, then just return null
  203. if (compressedBuffer.Length >= cache.Length)
  204. return null;
  205. return compressedBuffer;
  206. }
  207. int Adler32(byte[] bytes, int index, int length)
  208. {
  209. const uint a32mod = 65521;
  210. uint s1 = 1, s2 = 0;
  211. for (int i = index; i < length; i++)
  212. {
  213. byte b = bytes[i];
  214. s1 = (s1 + b) % a32mod;
  215. s2 = (s2 + s1) % a32mod;
  216. }
  217. return unchecked((int)((s2 << 16) + s1));
  218. }
  219. private void CompressAndSendCache()
  220. {
  221. long compressedLength, uncompressedLength;
  222. // we need to save the sequence byte that is written
  223. #if NETSTANDARD1_3
  224. byte[] cacheBuffer;
  225. ArraySegment<byte> cacheContentArraySegment;
  226. var cacheResult = cache.TryGetBuffer(out cacheContentArraySegment);
  227. if (cacheResult)
  228. cacheBuffer = cacheContentArraySegment.ToArray();
  229. else
  230. throw new InvalidDataException();
  231. #else
  232. byte[] cacheBuffer = cache.GetBuffer();
  233. #endif
  234. byte seq = cacheBuffer[3];
  235. cacheBuffer[3] = 0;
  236. // first we compress our current cache
  237. MemoryStream compressedBuffer = CompressCache();
  238. // now we set our compressed and uncompressed lengths
  239. // based on if our compression is going to help or not
  240. MemoryStream memStream;
  241. if (compressedBuffer == null)
  242. {
  243. compressedLength = cache.Length;
  244. uncompressedLength = 0;
  245. memStream = cache;
  246. }
  247. else
  248. {
  249. compressedLength = compressedBuffer.Length;
  250. uncompressedLength = cache.Length;
  251. memStream = compressedBuffer;
  252. }
  253. // Make space for length prefix (7 bytes) at the start of output
  254. long dataLength = memStream.Length;
  255. int bytesToWrite = (int)dataLength + 7;
  256. memStream.SetLength(bytesToWrite);
  257. #if NETSTANDARD1_3
  258. byte[] buffer;
  259. ArraySegment<byte> contentArraySegment;
  260. var result = memStream.TryGetBuffer(out contentArraySegment);
  261. if (result)
  262. buffer = contentArraySegment.ToArray();
  263. else
  264. throw new InvalidDataException();
  265. #else
  266. byte[] buffer = memStream.GetBuffer();
  267. #endif
  268. Array.Copy(buffer, 0, buffer, 7, (int)dataLength);
  269. // Write length prefix
  270. buffer[0] = (byte)(compressedLength & 0xff);
  271. buffer[1] = (byte)((compressedLength >> 8) & 0xff);
  272. buffer[2] = (byte)((compressedLength >> 16) & 0xff);
  273. buffer[3] = seq;
  274. buffer[4] = (byte)(uncompressedLength & 0xff);
  275. buffer[5] = (byte)((uncompressedLength >> 8) & 0xff);
  276. buffer[6] = (byte)((uncompressedLength >> 16) & 0xff);
  277. baseStream.Write(buffer, 0, bytesToWrite);
  278. baseStream.Flush();
  279. cache.SetLength(0);
  280. compressedBuffer?.Dispose();
  281. }
  282. public override void Flush()
  283. {
  284. if (!InputDone()) return;
  285. CompressAndSendCache();
  286. }
  287. private bool InputDone()
  288. {
  289. // if we have not done so yet, see if we can calculate how many bytes we are expecting
  290. if (baseStream is TimedStream && ((TimedStream)baseStream).IsClosed) return false;
  291. if (cache.Length < 4) return false;
  292. #if NETSTANDARD1_3
  293. byte[] buf;
  294. ArraySegment<byte> contentArraySegment;
  295. var result = cache.TryGetBuffer(out contentArraySegment);
  296. if (result)
  297. buf = contentArraySegment.ToArray();
  298. else
  299. throw new InvalidDataException();
  300. #else
  301. byte[] buf = cache.GetBuffer();
  302. #endif
  303. int expectedLen = buf[0] + (buf[1] << 8) + (buf[2] << 16);
  304. if (cache.Length < (expectedLen + 4)) return false;
  305. return true;
  306. }
  307. public override void WriteByte(byte value)
  308. {
  309. cache.WriteByte(value);
  310. }
  311. public override void Write(byte[] buffer, int offset, int count)
  312. {
  313. cache.Write(buffer, offset, count);
  314. }
  315. public override long Seek(long offset, SeekOrigin origin)
  316. {
  317. return baseStream.Seek(offset, origin);
  318. }
  319. }
  320. }
  321. #endif