From c86db64880d52630c372aa24f7aadf0036fb3fcf Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 4 Aug 2019 16:37:10 -0400 Subject: encapsulate gzip logic for reuse (#654) --- src/SMAPI.Web/Framework/Compression/GzipHelper.cs | 89 ++++++++++++++++++++++ src/SMAPI.Web/Framework/Compression/IGzipHelper.cs | 17 +++++ 2 files changed, 106 insertions(+) create mode 100644 src/SMAPI.Web/Framework/Compression/GzipHelper.cs create mode 100644 src/SMAPI.Web/Framework/Compression/IGzipHelper.cs (limited to 'src/SMAPI.Web/Framework/Compression') diff --git a/src/SMAPI.Web/Framework/Compression/GzipHelper.cs b/src/SMAPI.Web/Framework/Compression/GzipHelper.cs new file mode 100644 index 00000000..cc8f4737 --- /dev/null +++ b/src/SMAPI.Web/Framework/Compression/GzipHelper.cs @@ -0,0 +1,89 @@ +using System; +using System.IO; +using System.IO.Compression; +using System.Text; + +namespace StardewModdingAPI.Web.Framework.Compression +{ + /// Handles GZip compression logic. + internal class GzipHelper : IGzipHelper + { + /********* + ** Fields + *********/ + /// The first bytes in a valid zip file. + /// See . + private const uint GzipLeadBytes = 0x8b1f; + + + /********* + ** Public methods + *********/ + /// Compress a string. + /// The text to compress. + /// Derived from . + public string CompressString(string text) + { + // get raw bytes + byte[] buffer = Encoding.UTF8.GetBytes(text); + + // compressed + byte[] compressedData; + using (MemoryStream stream = new MemoryStream()) + { + using (GZipStream zipStream = new GZipStream(stream, CompressionLevel.Optimal, leaveOpen: true)) + zipStream.Write(buffer, 0, buffer.Length); + + stream.Position = 0; + compressedData = new byte[stream.Length]; + stream.Read(compressedData, 0, compressedData.Length); + } + + // prefix length + byte[] zipBuffer = new byte[compressedData.Length + 4]; + Buffer.BlockCopy(compressedData, 0, zipBuffer, 4, compressedData.Length); + Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, zipBuffer, 0, 4); + + // return string representation + return Convert.ToBase64String(zipBuffer); + } + + /// Decompress a string. + /// The compressed text. + /// Derived from . + public string DecompressString(string rawText) + { + // get raw bytes + byte[] zipBuffer; + try + { + zipBuffer = Convert.FromBase64String(rawText); + } + catch + { + return rawText; // not valid base64, wasn't compressed by the log parser + } + + // skip if not gzip + if (BitConverter.ToUInt16(zipBuffer, 4) != GzipHelper.GzipLeadBytes) + return rawText; + + // decompress + using (MemoryStream memoryStream = new MemoryStream()) + { + // read length prefix + int dataLength = BitConverter.ToInt32(zipBuffer, 0); + memoryStream.Write(zipBuffer, 4, zipBuffer.Length - 4); + + // read data + byte[] buffer = new byte[dataLength]; + memoryStream.Position = 0; + using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress)) + gZipStream.Read(buffer, 0, buffer.Length); + + // return original string + return Encoding.UTF8.GetString(buffer); + } + } + } +} diff --git a/src/SMAPI.Web/Framework/Compression/IGzipHelper.cs b/src/SMAPI.Web/Framework/Compression/IGzipHelper.cs new file mode 100644 index 00000000..a000865e --- /dev/null +++ b/src/SMAPI.Web/Framework/Compression/IGzipHelper.cs @@ -0,0 +1,17 @@ +namespace StardewModdingAPI.Web.Framework.Compression +{ + /// Handles GZip compression logic. + internal interface IGzipHelper + { + /********* + ** Methods + *********/ + /// Compress a string. + /// The text to compress. + string CompressString(string text); + + /// Decompress a string. + /// The compressed text. + string DecompressString(string rawText); + } +} -- cgit