diff --git a/Fuyu.Common/Fuyu.Common.csproj b/Fuyu.Common/Fuyu.Common.csproj index 63a10ca6..5fd247d4 100644 --- a/Fuyu.Common/Fuyu.Common.csproj +++ b/Fuyu.Common/Fuyu.Common.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net8.0;netstandard2.0 @@ -9,7 +9,7 @@ - + \ No newline at end of file diff --git a/Fuyu.Common/Networking/Context.cs b/Fuyu.Common/Networking/Context.cs index cbcee701..f7b08adb 100644 --- a/Fuyu.Common/Networking/Context.cs +++ b/Fuyu.Common/Networking/Context.cs @@ -1,9 +1,5 @@ using System.Collections.Generic; -using System.IO; using System.Net; -using System.Text; -using Fuyu.Common.Compression; -using Fuyu.Common.Serialization; namespace Fuyu.Common.Networking { diff --git a/Fuyu.Common/Networking/Controller.cs b/Fuyu.Common/Networking/Controller.cs index f84767cc..8de6a752 100644 --- a/Fuyu.Common/Networking/Controller.cs +++ b/Fuyu.Common/Networking/Controller.cs @@ -1,6 +1,4 @@ using System.Collections.Generic; -using System.Text; -using Fuyu.Common.Compression; namespace Fuyu.Common.Networking { diff --git a/Fuyu.Common/Networking/EftHttpClient.cs b/Fuyu.Common/Networking/EftHttpClient.cs index a7f14650..3c6c4cea 100644 --- a/Fuyu.Common/Networking/EftHttpClient.cs +++ b/Fuyu.Common/Networking/EftHttpClient.cs @@ -1,6 +1,6 @@ using System; using System.Net.Http; -using Fuyu.Common.Compression; +using Fuyu.Compression; namespace Fuyu.Common.Networking { @@ -15,14 +15,14 @@ public EftHttpClient(string address, string sessionId) : base(address) protected override byte[] OnSendBody(byte[] body) { - return Zlib.Compress(body, ZlibCompression.Level9); + return MemoryZlib.Compress(body, CompressionLevel.BestCompression); } protected override byte[] OnReceiveBody(byte[] body) { - if (Zlib.IsCompressed(body)) + if (MemoryZlib.IsCompressed(body)) { - body = Zlib.Decompress(body); + body = MemoryZlib.Decompress(body); } return body; diff --git a/Fuyu.Common/Networking/HttpContext.cs b/Fuyu.Common/Networking/HttpContext.cs index 8324d779..0502fc02 100644 --- a/Fuyu.Common/Networking/HttpContext.cs +++ b/Fuyu.Common/Networking/HttpContext.cs @@ -2,7 +2,7 @@ using System.Net; using System.Text; using System.Threading.Tasks; -using Fuyu.Common.Compression; +using Fuyu.Compression; using Fuyu.Common.Serialization; namespace Fuyu.Common.Networking @@ -25,9 +25,9 @@ public async Task GetBinaryAsync() await Request.InputStream.CopyToAsync(ms); var body = ms.ToArray(); - if (Zlib.IsCompressed(body)) + if (MemoryZlib.IsCompressed(body)) { - body = Zlib.Decompress(body); + body = MemoryZlib.Decompress(body); } return body; @@ -61,7 +61,7 @@ protected async Task SendAsync(byte[] data, string mime, HttpStatusCode status, if (zipped) { - data = Zlib.Compress(data, ZlibCompression.Level9); + data = MemoryZlib.Compress(data, CompressionLevel.BestCompression); } Response.StatusCode = (int)status; diff --git a/Fuyu.Common/Networking/WsContext.cs b/Fuyu.Common/Networking/WsContext.cs index 73056d7e..28d0d01b 100644 --- a/Fuyu.Common/Networking/WsContext.cs +++ b/Fuyu.Common/Networking/WsContext.cs @@ -1,13 +1,9 @@ using System; -using System.Collections.Generic; -using System.IO; using System.Net; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; -using Fuyu.Common.Compression; -using Fuyu.Common.Serialization; namespace Fuyu.Common.Networking { diff --git a/Fuyu.Compression/ComponentAce.Compression.Libs.zlib/SimpleZlib.cs b/Fuyu.Compression/ComponentAce.Compression.Libs.zlib/SimpleZlib.cs new file mode 100644 index 00000000..699a63cc --- /dev/null +++ b/Fuyu.Compression/ComponentAce.Compression.Libs.zlib/SimpleZlib.cs @@ -0,0 +1,100 @@ +using System; +using System.Text; +using Elskom.Generic.Libs; +using Fuyu.Compression; + +namespace ComponentAce.Compression.Libs.zlib +{ + public static class SimpleZlib + { + public const int bufsize = 4096; + + public static string Compress(string text, int compressLevel, Encoding encoding = null) + { + if (string.IsNullOrEmpty(text)) + { + return string.Empty; + } + + if (encoding == null) + { + encoding = Encoding.UTF8; + } + + var bytes = encoding.GetBytes(text); + var deflated = MemoryZlib.Compress(bytes, (CompressionLevel)compressLevel); + + return encoding.GetString(deflated); + } + + public static int CompressToBytesNonAlloc(string text, int compressLevel, byte[] encodingGetBytesBuffer, byte[] resultBuffer, Encoding encoding = null) + { + if (encoding == null) + { + encoding = Encoding.UTF8; + } + + _ = encoding.GetBytes(text, 0, text.Length, encodingGetBytesBuffer, 0); + resultBuffer = MemoryZlib.Compress(encodingGetBytesBuffer, (CompressionLevel)compressLevel); + + return resultBuffer.Length; + } + + public static byte[] CompressToBytes(string text, int compressLevel, Encoding encoding = null) + { + if (encoding == null) + { + encoding = Encoding.UTF8; + } + + var bytes = encoding.GetBytes(text); + + return MemoryZlib.Compress(bytes, (CompressionLevel)compressLevel); + } + + public static byte[] CompressToBytes(byte[] bytes, int length, int compressLevel) + { + return MemoryZlib.Compress(bytes, (CompressionLevel)compressLevel); + } + + public static int CompressWithZStream(ref ZStream zstream, byte[] bytes, int startIndex, int length, byte[] compressedBytes, int compressLevel) + { + throw new NotImplementedException(); + } + + public static string Decompress(string compressed, Encoding encoding = null) + { + if (string.IsNullOrEmpty(compressed)) + { + return string.Empty; + } + + if (encoding == null) + { + encoding = Encoding.UTF8; + } + + var bytes = encoding.GetBytes(compressed); + var inflated = MemoryZlib.Decompress(bytes); + + return encoding.GetString(inflated); + } + + public static string Decompress(byte[] bytes, Encoding encoding = null) + { + if (encoding == null) + { + encoding = Encoding.UTF8; + } + + var inflated = MemoryZlib.Decompress(bytes); + + return encoding.GetString(inflated); + } + + public static byte[] DecompressToBytes(byte[] compressedBytes) + { + return MemoryZlib.Decompress(compressedBytes); + } + } +} \ No newline at end of file diff --git a/Fuyu.Common/Compression/ZlibCompression.cs b/Fuyu.Compression/CompressionLevel.cs similarity index 60% rename from Fuyu.Common/Compression/ZlibCompression.cs rename to Fuyu.Compression/CompressionLevel.cs index f0aa8ac3..d02434ec 100644 --- a/Fuyu.Common/Compression/ZlibCompression.cs +++ b/Fuyu.Compression/CompressionLevel.cs @@ -1,58 +1,73 @@ -namespace Fuyu.Common.Compression +namespace Fuyu.Compression { /// /// Compression levels for zlib. /// - public enum ZlibCompression + public enum CompressionLevel { /// /// No compression. /// - NoCompression, + NoCompression = 0, /// /// Compression level 1. Best speed. /// - Level1, + Level1 = 1, /// /// Compression level 2. /// - Level2, + Level2 = 2, /// /// Compression level 3. /// - Level3, + Level3 = 3, /// /// Compression level 4. /// - Level4, + Level4 = 4, /// /// Compression level 5. /// - Level5, + Level5 = 5, /// /// Compression level 6. Default. /// - Level6, + Level6 = 6, /// /// Compression level 7. /// - Level7, + Level7 = 7, /// /// Compression level 8. /// - Level8, + Level8 = 8, /// /// Compression level 9. Highest compression. /// - Level9 + Level9 = 9, + + /// + /// Optimal balance. + /// + DefaultCompression = Level6, + + /// + /// Best speed. + /// + BestSpeed = Level1, + + /// + /// Highest compression. + /// + BestCompression = Level9 } } \ No newline at end of file diff --git a/Fuyu.Compression/Elskom/Adler32.cs b/Fuyu.Compression/Elskom/Adler32.cs new file mode 100644 index 00000000..21f94e7f --- /dev/null +++ b/Fuyu.Compression/Elskom/Adler32.cs @@ -0,0 +1,57 @@ +using System; + +namespace Elskom.Generic.Libs +{ + internal static class Adler32 + { + // largest prime smaller than 65536 + private const int BASE = 65521; + + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const int NMAX = 5552; + + public static long Calculate(long adler, ReadOnlySpan buffer, int index, int length) + { + if (buffer == null) + { + return 1L; + } + + var s1 = adler & 0xFFFF; + var s2 = (adler >> 16) & 0xFFFF; + int k; + + while (length > 0) + { + k = length < NMAX ? length : NMAX; + length -= k; + + while (k >= 16) + { + for (var i = 0; i < 16; ++i) + { + s1 += buffer[index++] & 0xFF; + s2 += s1; + } + + k -= 16; + } + + if (k != 0) + { + do + { + s1 += buffer[index++] & 0xFF; + s2 += s1; + } + while (--k != 0); + } + + s1 %= BASE; + s2 %= BASE; + } + + return (s2 << 16) | s1; + } + } +} \ No newline at end of file diff --git a/Fuyu.Compression/Elskom/CompressionFunction.cs b/Fuyu.Compression/Elskom/CompressionFunction.cs new file mode 100644 index 00000000..64ed39b9 --- /dev/null +++ b/Fuyu.Compression/Elskom/CompressionFunction.cs @@ -0,0 +1,9 @@ +namespace Elskom.Generic.Libs +{ + internal enum CompressionFunction + { + Stored = 0, + Fast = 1, + Slow = 2 + } +} \ No newline at end of file diff --git a/Fuyu.Compression/Elskom/CompressionStrategy.cs b/Fuyu.Compression/Elskom/CompressionStrategy.cs new file mode 100644 index 00000000..d026d9dd --- /dev/null +++ b/Fuyu.Compression/Elskom/CompressionStrategy.cs @@ -0,0 +1,20 @@ +namespace Elskom.Generic.Libs +{ + internal enum CompressionStrategy + { + /// + /// The default compression. Used for normal data. + /// + DefaultStrategy = 0, + + /// + /// Filtered compression. Used for data produced by a filter (or predictor). + /// + Filtered = 1, + + /// + /// Force Huffman encoding only (no string match). + /// + HuffmanOnly = 2 + } +} \ No newline at end of file diff --git a/Fuyu.Compression/Elskom/Config.cs b/Fuyu.Compression/Elskom/Config.cs new file mode 100644 index 00000000..31adf9b8 --- /dev/null +++ b/Fuyu.Compression/Elskom/Config.cs @@ -0,0 +1,27 @@ +namespace Elskom.Generic.Libs +{ + internal class Config + { + internal Config(int good_length, int max_lazy, int nice_length, int max_chain, CompressionFunction func) + { + this.GoodLength = good_length; + this.MaxLazy = max_lazy; + this.NiceLength = nice_length; + this.MaxChain = max_chain; + this.Func = func; + } + + // reduce lazy search above this match length + internal int GoodLength { get; set; } + + // do not perform lazy search above this match length + internal int MaxLazy { get; set; } + + // quit search above this match length + internal int NiceLength { get; set; } + + internal int MaxChain { get; set; } + + internal CompressionFunction Func { get; set; } + } +} \ No newline at end of file diff --git a/Fuyu.Compression/Elskom/Deflate.Fast.cs b/Fuyu.Compression/Elskom/Deflate.Fast.cs new file mode 100644 index 00000000..a6a2c2e3 --- /dev/null +++ b/Fuyu.Compression/Elskom/Deflate.Fast.cs @@ -0,0 +1,129 @@ +namespace Elskom.Generic.Libs +{ + /// + /// Class for compressing data through zlib. + /// + internal sealed partial class Deflate + { + // Compress as much as possible from the input stream, return the current + // block state. + // This function does not perform lazy evaluation of matches and inserts + // new strings in the dictionary only for unmatched strings or for short + // matches. It is used only for the fast compression options. + internal int Deflate_fast(int flush) + { + // short hash_head = 0; // head of the hash chain + var hash_head = 0; // head of the hash chain + bool bflush; // set if current block must be flushed + + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if (this.Lookahead < MINLOOKAHEAD) + { + this.Fill_window(); + if (this.Lookahead < MINLOOKAHEAD && flush == ZNOFLUSH) + { + return NeedMore; + } + + if (this.Lookahead == 0) + { + break; // flush the current block + } + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if (this.Lookahead >= MINMATCH) + { + this.InsH = ((this.InsH << this.HashShift) ^ (this.Window[this.Strstart + (MINMATCH - 1)] & 0xff)) & this.HashMask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = this.Head[this.InsH] & 0xffff; + this.Prev[this.Strstart & this.WMask] = this.Head[this.InsH]; + this.Head[this.InsH] = (short)this.Strstart; + } + + // Find the longest match, discarding those <= prev_length. + // At this point we have always match_length < MIN_MATCH + if (hash_head != 0L && ((this.Strstart - hash_head) & 0xffff) <= this.WSize - MINLOOKAHEAD) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if (this.Strategy != CompressionStrategy.HuffmanOnly) + { + this.MatchLength = this.Longest_match(hash_head); + } + + // longest_match() sets match_start + } + + if (this.MatchLength >= MINMATCH) + { + // check_match(strstart, match_start, match_length); + bflush = this.Tr_tally(this.Strstart - this.MatchStart, this.MatchLength - MINMATCH); + + this.Lookahead -= this.MatchLength; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if (this.MatchLength <= this.MaxLazyMatch && this.Lookahead >= MINMATCH) + { + this.MatchLength--; // string at strstart already in hash table + do + { + this.Strstart++; + + this.InsH = ((this.InsH << this.HashShift) ^ (this.Window[this.Strstart + (MINMATCH - 1)] & 0xff)) & this.HashMask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = this.Head[this.InsH] & 0xffff; + this.Prev[this.Strstart & this.WMask] = this.Head[this.InsH]; + this.Head[this.InsH] = (short)this.Strstart; + + // strstart never exceeds WSIZE-MAX_MATCH, so there are + // always MIN_MATCH bytes ahead. + } + while (--this.MatchLength != 0); + this.Strstart++; + } + else + { + this.Strstart += this.MatchLength; + this.MatchLength = 0; + this.InsH = this.Window[this.Strstart] & 0xff; + + this.InsH = ((this.InsH << this.HashShift) ^ (this.Window[this.Strstart + 1] & 0xff)) & this.HashMask; + + // If lookahead < MIN_MATCH, ins_h is garbage, but it does not + // matter since it will be recomputed at next deflate call. + } + } + else + { + // No match, output a literal byte + bflush = this.Tr_tally(0, this.Window[this.Strstart] & 0xff); + this.Lookahead--; + this.Strstart++; + } + + if (bflush) + { + this.Flush_block_only(false); + if (this.Strm.AvailOut == 0) + { + return NeedMore; + } + } + } + + this.Flush_block_only(flush == ZFINISH); + return this.Strm.AvailOut == 0 ? flush == ZFINISH ? FinishStarted : NeedMore : flush == ZFINISH ? FinishDone : BlockDone; + } + } +} \ No newline at end of file diff --git a/Fuyu.Compression/Elskom/Deflate.Slow.cs b/Fuyu.Compression/Elskom/Deflate.Slow.cs new file mode 100644 index 00000000..81e4c70a --- /dev/null +++ b/Fuyu.Compression/Elskom/Deflate.Slow.cs @@ -0,0 +1,154 @@ +namespace Elskom.Generic.Libs +{ + internal sealed partial class Deflate + { + // Same as above, but achieves better compression. We use a lazy + // evaluation for matches: a match is finally adopted only if there is + // no better match at the next window position. + internal int Deflate_slow(int flush) + { + // short hash_head = 0; // head of hash chain + var hash_head = 0; // head of hash chain + bool bflush; // set if current block must be flushed + + // Process the input block. + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if (this.Lookahead < MINLOOKAHEAD) + { + this.Fill_window(); + if (this.Lookahead < MINLOOKAHEAD && flush == ZNOFLUSH) + { + return NeedMore; + } + + if (this.Lookahead == 0) + { + break; // flush the current block + } + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if (this.Lookahead >= MINMATCH) + { + this.InsH = ((this.InsH << this.HashShift) ^ (this.Window[this.Strstart + (MINMATCH - 1)] & 0xff)) & this.HashMask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = this.Head[this.InsH] & 0xffff; + this.Prev[this.Strstart & this.WMask] = this.Head[this.InsH]; + this.Head[this.InsH] = (short)this.Strstart; + } + + // Find the longest match, discarding those <= prev_length. + this.PrevLength = this.MatchLength; + this.PrevMatch = this.MatchStart; + this.MatchLength = MINMATCH - 1; + + if (hash_head != 0 && this.PrevLength < this.MaxLazyMatch && ((this.Strstart - hash_head) & 0xffff) <= this.WSize - MINLOOKAHEAD) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if (this.Strategy != CompressionStrategy.HuffmanOnly) + { + this.MatchLength = this.Longest_match(hash_head); + } + + // longest_match() sets match_start + if (this.MatchLength <= 5 && (this.Strategy == CompressionStrategy.Filtered || (this.MatchLength == MINMATCH && this.Strstart - this.MatchStart > 4096))) + { + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + this.MatchLength = MINMATCH - 1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if (this.PrevLength >= MINMATCH && this.MatchLength <= this.PrevLength) + { + var max_insert = this.Strstart + this.Lookahead - MINMATCH; + + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + bflush = this.Tr_tally(this.Strstart - 1 - this.PrevMatch, this.PrevLength - MINMATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + this.Lookahead -= this.PrevLength - 1; + this.PrevLength -= 2; + do + { + if (++this.Strstart <= max_insert) + { + this.InsH = ((this.InsH << this.HashShift) ^ (this.Window[this.Strstart + (MINMATCH - 1)] & 0xff)) & this.HashMask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = this.Head[this.InsH] & 0xffff; + this.Prev[this.Strstart & this.WMask] = this.Head[this.InsH]; + this.Head[this.InsH] = (short)this.Strstart; + } + } + while (--this.PrevLength != 0); + this.MatchAvailable = 0; + this.MatchLength = MINMATCH - 1; + this.Strstart++; + + if (bflush) + { + this.Flush_block_only(false); + if (this.Strm.AvailOut == 0) + { + return NeedMore; + } + } + } + else if (this.MatchAvailable != 0) + { + // If there was no match at the previous position, output a + // single literal. If there was a match but the current match + // is longer, truncate the previous match to a single literal. + bflush = this.Tr_tally(0, this.Window[this.Strstart - 1] & 0xff); + + if (bflush) + { + this.Flush_block_only(false); + } + + this.Strstart++; + this.Lookahead--; + if (this.Strm.AvailOut == 0) + { + return NeedMore; + } + } + else + { + // There is no previous match to compare with, wait for + // the next step to decide. + this.MatchAvailable = 1; + this.Strstart++; + this.Lookahead--; + } + } + + if (this.MatchAvailable != 0) + { + bflush = this.Tr_tally(0, this.Window[this.Strstart - 1] & 0xff); + this.MatchAvailable = 0; + } + + this.Flush_block_only(flush == ZFINISH); + + return this.Strm.AvailOut == 0 ? flush == ZFINISH ? FinishStarted : NeedMore : flush == ZFINISH ? FinishDone : BlockDone; + } + } +} \ No newline at end of file diff --git a/Fuyu.Compression/Elskom/Deflate.Stored.cs b/Fuyu.Compression/Elskom/Deflate.Stored.cs new file mode 100644 index 00000000..502f0eef --- /dev/null +++ b/Fuyu.Compression/Elskom/Deflate.Stored.cs @@ -0,0 +1,76 @@ +namespace Elskom.Generic.Libs +{ + internal sealed partial class Deflate + { + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + internal int Deflate_stored(int flush) + { + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + var max_block_size = 0xffff; + int max_start; + + if (max_block_size > this.PendingBufSize - 5) + { + max_block_size = this.PendingBufSize - 5; + } + + // Copy as much as possible from input to output: + while (true) + { + // Fill the window as much as possible: + if (this.Lookahead <= 1) + { + this.Fill_window(); + if (this.Lookahead == 0 && flush == ZNOFLUSH) + { + return NeedMore; + } + + if (this.Lookahead == 0) + { + break; // flush the current block + } + } + + this.Strstart += this.Lookahead; + this.Lookahead = 0; + + // Emit a stored block if pending_buf will be full: + max_start = this.BlockStart + max_block_size; + if (this.Strstart == 0 || this.Strstart >= max_start) + { + // strstart == 0 is possible when wraparound on 16-bit machine + this.Lookahead = this.Strstart - max_start; + this.Strstart = max_start; + + this.Flush_block_only(false); + if (this.Strm.AvailOut == 0) + { + return NeedMore; + } + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if (this.Strstart - this.BlockStart >= this.WSize - MINLOOKAHEAD) + { + this.Flush_block_only(false); + if (this.Strm.AvailOut == 0) + { + return NeedMore; + } + } + } + + this.Flush_block_only(flush == ZFINISH); + return this.Strm.AvailOut == 0 ? (flush == ZFINISH) ? FinishStarted : NeedMore : flush == ZFINISH ? FinishDone : BlockDone; + } + } +} \ No newline at end of file diff --git a/Zlib.Managed/Elskom/Deflate.cs b/Fuyu.Compression/Elskom/Deflate.cs similarity index 74% rename from Zlib.Managed/Elskom/Deflate.cs rename to Fuyu.Compression/Elskom/Deflate.cs index 9b611ebe..8310e547 100644 --- a/Zlib.Managed/Elskom/Deflate.cs +++ b/Fuyu.Compression/Elskom/Deflate.cs @@ -1,16 +1,13 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. +using System; +using System.Collections.Generic; +using Fuyu.Compression; namespace Elskom.Generic.Libs { - using System; - /// /// Class for compressing data through zlib. /// - internal sealed class Deflate + internal sealed partial class Deflate { private const int MAXMEMLEVEL = 9; @@ -18,9 +15,6 @@ internal sealed class Deflate private const int MAXWBITS = 15; // 32K LZ77 window private const int DEFMEMLEVEL = 8; - private const int STORED = 0; - private const int FAST = 1; - private const int SLOW = 2; // block not completed, need more input or more output private const int NeedMore = 0; @@ -37,10 +31,6 @@ internal sealed class Deflate // preset dictionary flag in zlib header private const int PRESETDICT = 0x20; - private const int ZFILTERED = 1; - private const int ZHUFFMANONLY = 2; - private const int ZDEFAULTSTRATEGY = 0; - private const int ZNOFLUSH = 0; private const int ZPARTIALFLUSH = 1; @@ -100,7 +90,20 @@ internal sealed class Deflate private const int LCODES = LITERALS + 1 + LENGTHCODES; private const int HEAPSIZE = (2 * LCODES) + 1; - private static readonly Config[] ConfigTable; + private static readonly Dictionary ConfigTable = new Dictionary() + { + // level good lazy nice chain function + { CompressionLevel.NoCompression, new Config(0, 0, 0, 0, CompressionFunction.Stored) }, + { CompressionLevel.Level1, new Config(4, 4, 8, 4, CompressionFunction.Fast ) }, + { CompressionLevel.Level2, new Config(4, 5, 16, 8, CompressionFunction.Fast ) }, + { CompressionLevel.Level3, new Config(4, 6, 32, 32, CompressionFunction.Fast ) }, + { CompressionLevel.Level4, new Config(4, 4, 16, 16, CompressionFunction.Slow ) }, + { CompressionLevel.Level5, new Config(8, 16, 32, 32, CompressionFunction.Slow ) }, + { CompressionLevel.Level6, new Config(8, 16, 128, 128, CompressionFunction.Slow ) }, + { CompressionLevel.Level7, new Config(8, 32, 128, 256, CompressionFunction.Slow ) }, + { CompressionLevel.Level8, new Config(32, 128, 258, 1024, CompressionFunction.Slow ) }, + { CompressionLevel.Level9, new Config(32, 258, 258, 4096, CompressionFunction.Slow ) } + }; private static readonly string[] ZErrmsg = new string[] { @@ -109,26 +112,6 @@ internal sealed class Deflate string.Empty, }; - static Deflate() - { - { - ConfigTable = new Config[10]; - - // good lazy nice chain - ConfigTable[0] = new Config(0, 0, 0, 0, STORED); - ConfigTable[1] = new Config(4, 4, 8, 4, FAST); - ConfigTable[2] = new Config(4, 5, 16, 8, FAST); - ConfigTable[3] = new Config(4, 6, 32, 32, FAST); - - ConfigTable[4] = new Config(4, 4, 16, 16, SLOW); - ConfigTable[5] = new Config(8, 16, 32, 32, SLOW); - ConfigTable[6] = new Config(8, 16, 128, 128, SLOW); - ConfigTable[7] = new Config(8, 32, 128, 256, SLOW); - ConfigTable[8] = new Config(32, 128, 258, 1024, SLOW); - ConfigTable[9] = new Config(32, 258, 258, 4096, SLOW); - } - } - /// /// Initializes a new instance of the class. /// @@ -231,9 +214,9 @@ internal Deflate() // Insert new strings in the hash table only if the match length is not // greater than this length. This saves time but degrades compression. // max_insert_length is used only for compression levels <= 3. - internal int Level { get; private set; } // compression level (1..9) + internal CompressionLevel Level { get; private set; } // compression level (1..9) - internal int Strategy { get; private set; } // favor or force Huffman coding + internal CompressionStrategy Strategy { get; private set; } // favor or force Huffman coding // Use a faster search when the previous match is longer than this internal int GoodMatch { get; private set; } @@ -636,7 +619,7 @@ internal void PutShortMSB(int b) this.Put_byte((byte)b); } - internal void Send_code(int c, short[] tree) => this.Send_bits(tree[c * 2] & 0xffff, tree[(c * 2) + 1] & 0xffff); + internal void Send_code(int c, ReadOnlySpan tree) => this.Send_bits(tree[c * 2] & 0xffff, tree[(c * 2) + 1] & 0xffff); internal void Send_bits(int value_Renamed, int length) { @@ -714,7 +697,7 @@ internal bool Tr_tally(int dist, int lc) this.DynDtree[Tree.D_code(dist) * 2]++; } - if ((this.LastLit & 0x1fff) == 0 && this.Level > 2) + if ((this.LastLit & 0x1fff) == 0 && this.Level > CompressionLevel.Level2) { // Compute an upper bound for the compressed length var out_length = this.LastLit * 8; @@ -740,7 +723,7 @@ internal bool Tr_tally(int dist, int lc) } // Send the block data compressed using the given Huffman trees - internal void Compress_block(short[] ltree, short[] dtree) + internal void Compress_block(ReadOnlySpan ltree, ReadOnlySpan dtree) { int dist; // distance of matched string int lc; // match length or unmatched char (if dist == 0) @@ -884,77 +867,6 @@ internal void Flush_block_only(bool eof) this.Strm.Flush_pending(); } - // Copy without compression as much as possible from the input stream, return - // the current block state. - // This function does not insert new strings in the dictionary since - // uncompressible data is probably not useful. This function is used - // only for the level=0 compression option. - // NOTE: this function should be optimized to avoid extra copying from - // window to pending_buf. - internal int Deflate_stored(int flush) - { - // Stored blocks are limited to 0xffff bytes, pending_buf is limited - // to pending_buf_size, and each stored block has a 5 byte header: - var max_block_size = 0xffff; - int max_start; - - if (max_block_size > this.PendingBufSize - 5) - { - max_block_size = this.PendingBufSize - 5; - } - - // Copy as much as possible from input to output: - while (true) - { - // Fill the window as much as possible: - if (this.Lookahead <= 1) - { - this.Fill_window(); - if (this.Lookahead == 0 && flush == ZNOFLUSH) - { - return NeedMore; - } - - if (this.Lookahead == 0) - { - break; // flush the current block - } - } - - this.Strstart += this.Lookahead; - this.Lookahead = 0; - - // Emit a stored block if pending_buf will be full: - max_start = this.BlockStart + max_block_size; - if (this.Strstart == 0 || this.Strstart >= max_start) - { - // strstart == 0 is possible when wraparound on 16-bit machine - this.Lookahead = this.Strstart - max_start; - this.Strstart = max_start; - - this.Flush_block_only(false); - if (this.Strm.AvailOut == 0) - { - return NeedMore; - } - } - - // Flush if we may have to slide, otherwise block_start may become - // negative and the data will be gone: - if (this.Strstart - this.BlockStart >= this.WSize - MINLOOKAHEAD) - { - this.Flush_block_only(false); - if (this.Strm.AvailOut == 0) - { - return NeedMore; - } - } - } - - this.Flush_block_only(flush == ZFINISH); - return this.Strm.AvailOut == 0 ? (flush == ZFINISH) ? FinishStarted : NeedMore : flush == ZFINISH ? FinishDone : BlockDone; - } - // Send a stored block internal void Tr_stored_block(int buf, int stored_len, bool eof) { @@ -1137,276 +1049,6 @@ internal void Fill_window() while (this.Lookahead < MINLOOKAHEAD && this.Strm.AvailIn != 0); } - // Compress as much as possible from the input stream, return the current - // block state. - // This function does not perform lazy evaluation of matches and inserts - // new strings in the dictionary only for unmatched strings or for short - // matches. It is used only for the fast compression options. - internal int Deflate_fast(int flush) - { - // short hash_head = 0; // head of the hash chain - var hash_head = 0; // head of the hash chain - bool bflush; // set if current block must be flushed - - while (true) - { - // Make sure that we always have enough lookahead, except - // at the end of the input file. We need MAX_MATCH bytes - // for the next match, plus MIN_MATCH bytes to insert the - // string following the next match. - if (this.Lookahead < MINLOOKAHEAD) - { - this.Fill_window(); - if (this.Lookahead < MINLOOKAHEAD && flush == ZNOFLUSH) - { - return NeedMore; - } - - if (this.Lookahead == 0) - { - break; // flush the current block - } - } - - // Insert the string window[strstart .. strstart+2] in the - // dictionary, and set hash_head to the head of the hash chain: - if (this.Lookahead >= MINMATCH) - { - this.InsH = ((this.InsH << this.HashShift) ^ (this.Window[this.Strstart + (MINMATCH - 1)] & 0xff)) & this.HashMask; - - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = this.Head[this.InsH] & 0xffff; - this.Prev[this.Strstart & this.WMask] = this.Head[this.InsH]; - this.Head[this.InsH] = (short)this.Strstart; - } - - // Find the longest match, discarding those <= prev_length. - // At this point we have always match_length < MIN_MATCH - if (hash_head != 0L && ((this.Strstart - hash_head) & 0xffff) <= this.WSize - MINLOOKAHEAD) - { - // To simplify the code, we prevent matches with the string - // of window index 0 (in particular we have to avoid a match - // of the string with itself at the start of the input file). - if (this.Strategy != ZHUFFMANONLY) - { - this.MatchLength = this.Longest_match(hash_head); - } - - // longest_match() sets match_start - } - - if (this.MatchLength >= MINMATCH) - { - // check_match(strstart, match_start, match_length); - bflush = this.Tr_tally(this.Strstart - this.MatchStart, this.MatchLength - MINMATCH); - - this.Lookahead -= this.MatchLength; - - // Insert new strings in the hash table only if the match length - // is not too large. This saves time but degrades compression. - if (this.MatchLength <= this.MaxLazyMatch && this.Lookahead >= MINMATCH) - { - this.MatchLength--; // string at strstart already in hash table - do - { - this.Strstart++; - - this.InsH = ((this.InsH << this.HashShift) ^ (this.Window[this.Strstart + (MINMATCH - 1)] & 0xff)) & this.HashMask; - - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = this.Head[this.InsH] & 0xffff; - this.Prev[this.Strstart & this.WMask] = this.Head[this.InsH]; - this.Head[this.InsH] = (short)this.Strstart; - - // strstart never exceeds WSIZE-MAX_MATCH, so there are - // always MIN_MATCH bytes ahead. - } - while (--this.MatchLength != 0); - this.Strstart++; - } - else - { - this.Strstart += this.MatchLength; - this.MatchLength = 0; - this.InsH = this.Window[this.Strstart] & 0xff; - - this.InsH = ((this.InsH << this.HashShift) ^ (this.Window[this.Strstart + 1] & 0xff)) & this.HashMask; - - // If lookahead < MIN_MATCH, ins_h is garbage, but it does not - // matter since it will be recomputed at next deflate call. - } - } - else - { - // No match, output a literal byte - bflush = this.Tr_tally(0, this.Window[this.Strstart] & 0xff); - this.Lookahead--; - this.Strstart++; - } - - if (bflush) - { - this.Flush_block_only(false); - if (this.Strm.AvailOut == 0) - { - return NeedMore; - } - } - } - - this.Flush_block_only(flush == ZFINISH); - return this.Strm.AvailOut == 0 ? flush == ZFINISH ? FinishStarted : NeedMore : flush == ZFINISH ? FinishDone : BlockDone; - } - - // Same as above, but achieves better compression. We use a lazy - // evaluation for matches: a match is finally adopted only if there is - // no better match at the next window position. - internal int Deflate_slow(int flush) - { - // short hash_head = 0; // head of hash chain - var hash_head = 0; // head of hash chain - bool bflush; // set if current block must be flushed - - // Process the input block. - while (true) - { - // Make sure that we always have enough lookahead, except - // at the end of the input file. We need MAX_MATCH bytes - // for the next match, plus MIN_MATCH bytes to insert the - // string following the next match. - if (this.Lookahead < MINLOOKAHEAD) - { - this.Fill_window(); - if (this.Lookahead < MINLOOKAHEAD && flush == ZNOFLUSH) - { - return NeedMore; - } - - if (this.Lookahead == 0) - { - break; // flush the current block - } - } - - // Insert the string window[strstart .. strstart+2] in the - // dictionary, and set hash_head to the head of the hash chain: - if (this.Lookahead >= MINMATCH) - { - this.InsH = ((this.InsH << this.HashShift) ^ (this.Window[this.Strstart + (MINMATCH - 1)] & 0xff)) & this.HashMask; - - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = this.Head[this.InsH] & 0xffff; - this.Prev[this.Strstart & this.WMask] = this.Head[this.InsH]; - this.Head[this.InsH] = (short)this.Strstart; - } - - // Find the longest match, discarding those <= prev_length. - this.PrevLength = this.MatchLength; - this.PrevMatch = this.MatchStart; - this.MatchLength = MINMATCH - 1; - - if (hash_head != 0 && this.PrevLength < this.MaxLazyMatch && ((this.Strstart - hash_head) & 0xffff) <= this.WSize - MINLOOKAHEAD) - { - // To simplify the code, we prevent matches with the string - // of window index 0 (in particular we have to avoid a match - // of the string with itself at the start of the input file). - if (this.Strategy != ZHUFFMANONLY) - { - this.MatchLength = this.Longest_match(hash_head); - } - - // longest_match() sets match_start - if (this.MatchLength <= 5 && (this.Strategy == ZFILTERED || (this.MatchLength == MINMATCH && this.Strstart - this.MatchStart > 4096))) - { - // If prev_match is also MIN_MATCH, match_start is garbage - // but we will ignore the current match anyway. - this.MatchLength = MINMATCH - 1; - } - } - - // If there was a match at the previous step and the current - // match is not better, output the previous match: - if (this.PrevLength >= MINMATCH && this.MatchLength <= this.PrevLength) - { - var max_insert = this.Strstart + this.Lookahead - MINMATCH; - - // Do not insert strings in hash table beyond this. - - // check_match(strstart-1, prev_match, prev_length); - bflush = this.Tr_tally(this.Strstart - 1 - this.PrevMatch, this.PrevLength - MINMATCH); - - // Insert in hash table all strings up to the end of the match. - // strstart-1 and strstart are already inserted. If there is not - // enough lookahead, the last two strings are not inserted in - // the hash table. - this.Lookahead -= this.PrevLength - 1; - this.PrevLength -= 2; - do - { - if (++this.Strstart <= max_insert) - { - this.InsH = ((this.InsH << this.HashShift) ^ (this.Window[this.Strstart + (MINMATCH - 1)] & 0xff)) & this.HashMask; - - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = this.Head[this.InsH] & 0xffff; - this.Prev[this.Strstart & this.WMask] = this.Head[this.InsH]; - this.Head[this.InsH] = (short)this.Strstart; - } - } - while (--this.PrevLength != 0); - this.MatchAvailable = 0; - this.MatchLength = MINMATCH - 1; - this.Strstart++; - - if (bflush) - { - this.Flush_block_only(false); - if (this.Strm.AvailOut == 0) - { - return NeedMore; - } - } - } - else if (this.MatchAvailable != 0) - { - // If there was no match at the previous position, output a - // single literal. If there was a match but the current match - // is longer, truncate the previous match to a single literal. - bflush = this.Tr_tally(0, this.Window[this.Strstart - 1] & 0xff); - - if (bflush) - { - this.Flush_block_only(false); - } - - this.Strstart++; - this.Lookahead--; - if (this.Strm.AvailOut == 0) - { - return NeedMore; - } - } - else - { - // There is no previous match to compare with, wait for - // the next step to decide. - this.MatchAvailable = 1; - this.Strstart++; - this.Lookahead--; - } - } - - if (this.MatchAvailable != 0) - { - bflush = this.Tr_tally(0, this.Window[this.Strstart - 1] & 0xff); - this.MatchAvailable = 0; - } - - this.Flush_block_only(flush == ZFINISH); - - return this.Strm.AvailOut == 0 ? flush == ZFINISH ? FinishStarted : NeedMore : flush == ZFINISH ? FinishDone : BlockDone; - } - internal int Longest_match(int cur_match) { var chain_length = this.MaxChainLength; // max hash chain length @@ -1488,11 +1130,13 @@ internal int Longest_match(int cur_match) return best_len <= this.Lookahead ? best_len : this.Lookahead; } - internal int DeflateInit(ZStream strm, int level, int bits) => this.DeflateInit2(strm, level, ZDEFLATED, bits, DEFMEMLEVEL, ZDEFAULTSTRATEGY); + internal int DeflateInit(ZStream strm, CompressionLevel level, int bits) + => this.DeflateInit2(strm, level, ZDEFLATED, bits, DEFMEMLEVEL, CompressionStrategy.DefaultStrategy); - internal int DeflateInit(ZStream strm, int level) => this.DeflateInit(strm, level, MAXWBITS); + internal int DeflateInit(ZStream strm, CompressionLevel level) + => this.DeflateInit(strm, level, MAXWBITS); - internal int DeflateInit2(ZStream strm, int level, int method, int windowBits, int memLevel, int strategy) + internal int DeflateInit2(ZStream strm, CompressionLevel level, int method, int windowBits, int memLevel, CompressionStrategy strategy) { var noheader = 0; @@ -1505,11 +1149,6 @@ internal int DeflateInit2(ZStream strm, int level, int method, int windowBits, i // } strm.Msg = null; - if (level == ZDEFAULTCOMPRESSION) - { - level = 6; - } - if (windowBits < 0) { // undocumented feature: suppress zlib header @@ -1517,7 +1156,7 @@ internal int DeflateInit2(ZStream strm, int level, int method, int windowBits, i windowBits = -windowBits; } - if (memLevel < 1 || memLevel > MAXMEMLEVEL || method != ZDEFLATED || windowBits < 9 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > ZHUFFMANONLY) + if (memLevel < 1 || memLevel > MAXMEMLEVEL || method != ZDEFLATED || windowBits < 9 || windowBits > 15 || level < CompressionLevel.NoCompression || level > CompressionLevel.Level9 || strategy < 0 || strategy > CompressionStrategy.HuffmanOnly) { return ZSTREAMERROR; } @@ -1599,16 +1238,11 @@ internal int DeflateEnd() return this.Status == BUSYSTATE ? ZDATAERROR : ZOK; } - internal int DeflateParams(ZStream strm, int level, int strategy) + internal int DeflateParams(ZStream strm, CompressionLevel level, CompressionStrategy strategy) { var err = ZOK; - if (level == ZDEFAULTCOMPRESSION) - { - level = 6; - } - - if (level < 0 || level > 9 || strategy < 0 || strategy > ZHUFFMANONLY) + if (level < CompressionLevel.NoCompression || level > CompressionLevel.Level9 || strategy < 0 || strategy > CompressionStrategy.HuffmanOnly) { return ZSTREAMERROR; } @@ -1632,7 +1266,8 @@ internal int DeflateParams(ZStream strm, int level, int strategy) return err; } - internal int DeflateSetDictionary(ZStream strm, byte[] dictionary, int dictLength) + // TODO: Check if ReadOnlySpan dictionary is okay + internal int DeflateSetDictionary(ZStream strm, ReadOnlySpan dictionary, int dictLength) { var length = dictLength; var index = 0; @@ -1655,7 +1290,11 @@ internal int DeflateSetDictionary(ZStream strm, byte[] dictionary, int dictLengt index = dictLength - length; // use the tail of the dictionary } - Array.Copy(dictionary, index, this.Window, 0, length); + // Array.Copy(dictionary.ToArray(), index, this.Window, 0, length); + var source = dictionary.Slice(index, length); + var target = new Span(this.Window, 0, length); + source.CopyTo(target); + this.Strstart = length; this.BlockStart = length; @@ -1704,7 +1343,7 @@ internal int Compress(ZStream strm, int flush) if (this.Status == INITSTATE) { var header = (ZDEFLATED + ((this.WBits - 8) << 4)) << 8; - var level_flags = ((this.Level - 1) & 0xff) >> 1; + var level_flags = ((int)(this.Level - 1) & 0xff) >> 1; if (level_flags > 3) { @@ -1771,15 +1410,15 @@ internal int Compress(ZStream strm, int flush) var bstate = -1; switch (ConfigTable[this.Level].Func) { - case STORED: + case CompressionFunction.Stored: bstate = this.Deflate_stored(flush); break; - case FAST: + case CompressionFunction.Fast: bstate = this.Deflate_fast(flush); break; - case SLOW: + case CompressionFunction.Slow: bstate = this.Deflate_slow(flush); break; @@ -1862,30 +1501,5 @@ internal int Compress(ZStream strm, int flush) this.Noheader = -1; // write the trailer only once! return this.Pending != 0 ? ZOK : ZSTREAMEND; } - - private class Config - { - internal Config(int good_length, int max_lazy, int nice_length, int max_chain, int func) - { - this.GoodLength = good_length; - this.MaxLazy = max_lazy; - this.NiceLength = nice_length; - this.MaxChain = max_chain; - this.Func = func; - } - - // reduce lazy search above this match length - internal int GoodLength { get; set; } - - // do not perform lazy search above this match length - internal int MaxLazy { get; set; } - - // quit search above this match length - internal int NiceLength { get; set; } - - internal int MaxChain { get; set; } - - internal int Func { get; set; } - } } } \ No newline at end of file diff --git a/Zlib.Managed/Elskom/InfBlocks.cs b/Fuyu.Compression/Elskom/InfBlocks.cs similarity index 98% rename from Zlib.Managed/Elskom/InfBlocks.cs rename to Fuyu.Compression/Elskom/InfBlocks.cs index e9a3a936..19061f57 100644 --- a/Zlib.Managed/Elskom/InfBlocks.cs +++ b/Fuyu.Compression/Elskom/InfBlocks.cs @@ -1,16 +1,11 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. +using System; namespace Elskom.Generic.Libs { - using System; - internal sealed class InfBlocks { // Table for deflate from PKZIP's appnote.txt. - internal static readonly int[] Border = new int[] + internal static ReadOnlySpan Border => new[] { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15, }; @@ -40,7 +35,7 @@ internal sealed class InfBlocks private const int BAD = 9; // ot a data error--stuck here // And'ing with mask[n] masks the lower n bits - private static readonly int[] InflateMask = new int[] + private static ReadOnlySpan InflateMask => new[] { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, @@ -689,9 +684,14 @@ internal void Free(ZStream z) // ZFREE(z, s); } - internal void Set_dictionary(byte[] d, int start, int n) + // TODO: Check if ReadOnlySpan d is okay + internal void Set_dictionary(ReadOnlySpan d, int start, int n) { - Array.Copy(d, start, this.Window, 0, n); + //Array.Copy(d, start, this.Window, 0, n); + var source = d.Slice(start); + var target = new Span(this.Window, 0, n); + source.CopyTo(target); + this.Read = this.Write = n; } diff --git a/Zlib.Managed/Elskom/InfCodes.cs b/Fuyu.Compression/Elskom/InfCodes.cs similarity index 99% rename from Zlib.Managed/Elskom/InfCodes.cs rename to Fuyu.Compression/Elskom/InfCodes.cs index e5212451..0bcbda2f 100644 --- a/Zlib.Managed/Elskom/InfCodes.cs +++ b/Fuyu.Compression/Elskom/InfCodes.cs @@ -1,12 +1,7 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. +using System; namespace Elskom.Generic.Libs { - using System; - internal sealed class InfCodes { private const int ZOK = 0; @@ -34,7 +29,7 @@ internal sealed class InfCodes private const int END = 8; // x: got eob and all data flushed private const int BADCODE = 9; // x: got error - private static readonly int[] InflateMask = new int[] + private static ReadOnlySpan InflateMask => new[] { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, diff --git a/Zlib.Managed/Elskom/InfTree.cs b/Fuyu.Compression/Elskom/InfTree.cs similarity index 96% rename from Zlib.Managed/Elskom/InfTree.cs rename to Fuyu.Compression/Elskom/InfTree.cs index 27372872..4f6aac03 100644 --- a/Zlib.Managed/Elskom/InfTree.cs +++ b/Fuyu.Compression/Elskom/InfTree.cs @@ -1,12 +1,7 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. +using System; namespace Elskom.Generic.Libs { - using System; - internal static class InfTree { internal const int FixedBl = 9; @@ -15,7 +10,7 @@ internal static class InfTree // If BMAX needs to be larger than 16, then h and x[] should be uLong. internal const int BMAX = 15; // maximum bit length of any code - internal static readonly int[] FixedTl = new int[] + internal static ReadOnlySpan FixedTl => new[] { 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 192, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 160, 0, 8, 0, 0, 8, @@ -88,7 +83,7 @@ internal static class InfTree 143, 0, 8, 79, 0, 9, 255, }; - internal static readonly int[] FixedTd = new int[] + internal static ReadOnlySpan FixedTd => new[] { 80, 5, 1, 87, 5, 257, 83, 5, 17, 91, 5, 4097, 81, 5, 5, 89, 5, 1025, 85, 5, 65, 93, 5, 16385, 80, 5, 3, 88, 5, 513, 84, 5, 33, 92, 5, 8193, 82, 5, 9, 90, 5, @@ -98,25 +93,25 @@ internal static class InfTree }; // Tables for deflate from PKZIP's appnote.txt. - internal static readonly int[] Cplens = new int[] + internal static ReadOnlySpan Cplens => new[] { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0, }; - internal static readonly int[] Cplext = new int[] + internal static ReadOnlySpan Cplext => new[] { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112, }; - internal static readonly int[] Cpdist = new int[] + internal static ReadOnlySpan Cpdist => new[] { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, }; - internal static readonly int[] Cpdext = new int[] + internal static ReadOnlySpan Cpdext => new[] { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, @@ -134,7 +129,7 @@ internal static class InfTree private const int ZBUFERROR = -5; // private const int ZVERSIONERROR = -6; - internal static int Huft_build(int[] b, int bindex, int n, int s, int[] d, int[] e, int[] t, int[] m, int[] hp, int[] hn, int[] v) + internal static int Huft_build(int[] b, int bindex, int n, int s, ReadOnlySpan d, ReadOnlySpan e, int[] t, int[] m, int[] hp, int[] hn, int[] v) { // Given a list of code lengths and a maximum table size, make a set of // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR @@ -450,8 +445,8 @@ internal static int Inflate_trees_fixed(int[] bl, int[] bd, int[][] tl, int[][] { bl[0] = FixedBl; bd[0] = FixedBd; - tl[0] = FixedTl; - td[0] = FixedTd; + tl[0] = FixedTl.ToArray(); + td[0] = FixedTd.ToArray(); return ZOK; } } diff --git a/Zlib.Managed/Elskom/Inflate.cs b/Fuyu.Compression/Elskom/Inflate.cs similarity index 97% rename from Zlib.Managed/Elskom/Inflate.cs rename to Fuyu.Compression/Elskom/Inflate.cs index 051aac6b..307a0229 100644 --- a/Zlib.Managed/Elskom/Inflate.cs +++ b/Fuyu.Compression/Elskom/Inflate.cs @@ -1,7 +1,4 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. +using System; namespace Elskom.Generic.Libs { @@ -48,7 +45,10 @@ internal sealed class Inflate private const int DONE = 12; // finished check, done private const int BAD = 13; // got an error--stay here - private static readonly byte[] Mark = new byte[] { 0, 0, (byte)SupportClass.Identity(0xff), (byte)SupportClass.Identity(0xff) }; + private static ReadOnlySpan Mark => new byte[] + { + 0, 0, (byte)SupportClass.Identity(0xff), (byte)SupportClass.Identity(0xff) + }; internal int Mode { get; private set; } // current inflate mode @@ -335,7 +335,7 @@ internal static int Decompress(ZStream z, int f) } } - internal static int InflateSetDictionary(ZStream z, byte[] dictionary, int dictLength) + internal static int InflateSetDictionary(ZStream z, Span dictionary, int dictLength) { var index = 0; var length = dictLength; diff --git a/Zlib.Managed/Elskom/StaticTree.cs b/Fuyu.Compression/Elskom/StaticTree.cs similarity index 76% rename from Zlib.Managed/Elskom/StaticTree.cs rename to Fuyu.Compression/Elskom/StaticTree.cs index b8b5838d..2382d624 100644 --- a/Zlib.Managed/Elskom/StaticTree.cs +++ b/Fuyu.Compression/Elskom/StaticTree.cs @@ -1,13 +1,10 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. +using System; namespace Elskom.Generic.Libs { internal sealed class StaticTree { - internal static readonly short[] StaticLtree = new short[] + internal static ReadOnlySpan StaticLtree => new short[] { 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, 252, 8, 2, 8, 130, 8, 66, 8, 194, 8, 34, 8, @@ -39,19 +36,18 @@ internal sealed class StaticTree 195, 8, 35, 8, 163, 8, 99, 8, 227, 8, }; - internal static readonly short[] StaticDtree = new short[] + internal static ReadOnlySpan StaticDtree => new short[] { 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, 30, 5, 1, 5, 17, 5, 9, 5, 25, 5, 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, 19, 5, 11, 5, 27, 5, 7, 5, 23, 5, }; - internal static readonly StaticTree StaticLDesc; + internal static readonly StaticTree StaticLDesc = new StaticTree(0, 0, 257, 286, 15); - internal static readonly StaticTree StaticDDesc; - - internal static readonly StaticTree StaticBlDesc; + internal static readonly StaticTree StaticDDesc = new StaticTree(1, 1, 0, 30, 15); + internal static readonly StaticTree StaticBlDesc = new StaticTree(2, 2, 0, 19, 7); private const int MAXBITS = 15; private const int BLCODES = 19; @@ -63,13 +59,6 @@ internal sealed class StaticTree private const int MAXBLBITS = 7; private const int LCODES = LITERALS + 1 + LENGTHCODES; - static StaticTree() - { - StaticLDesc = new StaticTree(StaticLtree, Tree.ExtraLbits, LITERALS + 1, LCODES, MAXBITS); - StaticDDesc = new StaticTree(StaticDtree, Tree.ExtraDbits, 0, DCODES, MAXBITS); - StaticBlDesc = new StaticTree(null, Tree.ExtraBlbits, 0, BLCODES, MAXBLBITS); - } - /// /// Initializes a new instance of the class. /// @@ -78,18 +67,18 @@ static StaticTree() /// extra base. /// elements?. /// max length. - internal StaticTree(short[] static_tree, int[] extra_bits, int extra_base, int elems, int max_length) + internal StaticTree(int staticTreeOption, int extraBitOption, int extraBase, int elems, int maxLength) { - this.StaticTreeValue = static_tree; - this.ExtraBits = extra_bits; - this.ExtraBase = extra_base; + this.StaticTreeOption = staticTreeOption; + this.ExtraBitOption = extraBitOption; + this.ExtraBase = extraBase; this.Elems = elems; - this.MaxLength = max_length; + this.MaxLength = maxLength; } - internal short[] StaticTreeValue { get; private set; } // static tree or null + internal int StaticTreeOption { get; } - internal int[] ExtraBits { get; private set; } // extra bits for each code or null + internal int ExtraBitOption { get; } internal int ExtraBase { get; private set; } // base index for extra_bits diff --git a/Zlib.Managed/Elskom/SupportClass.cs b/Fuyu.Compression/Elskom/SupportClass.cs similarity index 87% rename from Zlib.Managed/Elskom/SupportClass.cs rename to Fuyu.Compression/Elskom/SupportClass.cs index ccaf937b..8c21c09f 100644 --- a/Zlib.Managed/Elskom/SupportClass.cs +++ b/Fuyu.Compression/Elskom/SupportClass.cs @@ -1,14 +1,8 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. +using System; +using System.IO; namespace Elskom.Generic.Libs { - using System; - using System.IO; - using System.Text; - /// /// Class to support zlib stuff. /// @@ -85,7 +79,7 @@ internal static class SupportClass /// The starting index of the target array. /// The maximum number of characters to read from the source Stream. /// The number of characters read. The number will be less than or equal to count depending on the data available in the source Stream. Returns -1 if the end of the stream is reached. - internal static int ReadInput(Stream sourceStream, byte[] target, int start, int count) + internal static int ReadInput(Stream sourceStream, Span target, int start, int count) { if (sourceStream == null) { @@ -127,7 +121,7 @@ internal static int ReadInput(Stream sourceStream, byte[] target, int start, int /// The starting index of the target array. /// The maximum number of characters to read from the source TextReader. /// The number of characters read. The number will be less than or equal to count depending on the data available in the source TextReader. Returns -1 if the end of the stream is reached. - internal static int ReadInput(TextReader sourceTextReader, byte[] target, int start, int count) + internal static int ReadInput(TextReader sourceTextReader, Span target, int start, int count) { if (sourceTextReader == null) { @@ -161,19 +155,5 @@ internal static int ReadInput(TextReader sourceTextReader, byte[] target, int st return bytesRead; } - - /// - /// Converts a string to an array of bytes. - /// - /// The string to be converted. - /// The new array of bytes. - internal static byte[] ToByteArray(string sourceString) => Encoding.UTF8.GetBytes(sourceString); - - /// - /// Converts an array of bytes to an array of chars. - /// - /// The array of bytes to convert. - /// The new array of chars. - internal static char[] ToCharArray(byte[] byteArray) => Encoding.UTF8.GetChars(byteArray); } } diff --git a/Zlib.Managed/Elskom/Tree.cs b/Fuyu.Compression/Elskom/Tree.cs similarity index 84% rename from Zlib.Managed/Elskom/Tree.cs rename to Fuyu.Compression/Elskom/Tree.cs index abc91121..5214046c 100644 --- a/Zlib.Managed/Elskom/Tree.cs +++ b/Fuyu.Compression/Elskom/Tree.cs @@ -1,7 +1,4 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. +using System; namespace Elskom.Generic.Libs { @@ -31,31 +28,44 @@ internal sealed class Tree internal const int DISTCODELEN = 512; // extra bits for each length code - internal static readonly int[] ExtraLbits = new int[] + internal static ReadOnlySpan ExtraLbits => new[] { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, }; // extra bits for each distance code - internal static readonly int[] ExtraDbits = new int[] + internal static ReadOnlySpan ExtraDbits => new[] { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, }; // extra bits for each bit length code - internal static readonly int[] ExtraBlbits = new int[] + internal static ReadOnlySpan ExtraBlbits => new[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7, }; - internal static readonly byte[] BlOrder = new byte[] + internal static ReadOnlySpan BaseLength => new[] + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 160, 192, 224, 0, + }; + + internal static ReadOnlySpan BaseDist => new[] + { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, + 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, + 24576, + }; + + internal static ReadOnlySpan BlOrder => new byte[] { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15, }; - internal static readonly byte[] DistCode = new byte[] + internal static ReadOnlySpan DistCode => new byte[] { 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, @@ -70,7 +80,7 @@ internal sealed class Tree 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, @@ -87,36 +97,23 @@ internal sealed class Tree 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, }; - internal static readonly byte[] LengthCode = new byte[] - { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13, - 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, - 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, - 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, - 28, - }; - - internal static readonly int[] BaseLength = new int[] + internal static ReadOnlySpan LengthCode => new byte[] { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, - 64, 80, 96, 112, 128, 160, 192, 224, 0, - }; - - internal static readonly int[] BaseDist = new int[] - { - 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, - 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, - 24576, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, + 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, + 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, }; private const int MAXBITS = 15; @@ -204,8 +201,38 @@ internal static int Bi_reverse(int code, int len) internal void Gen_bitlen(Deflate s) { var tree = this.DynTree; - var stree = this.StatDesc.StaticTreeValue; - var extra = this.StatDesc.ExtraBits; + ReadOnlySpan stree; + switch (this.StatDesc.StaticTreeOption) + { + case 0: + stree = StaticTree.StaticLtree; + break; + + case 1: + stree = StaticTree.StaticDtree; + break; + + case 2: + default: + stree = null; + break; + }; + ReadOnlySpan extra; + switch (this.StatDesc.ExtraBitOption) + { + case 0: + extra = ExtraLbits; + break; + + case 1: + extra = ExtraDbits; + break; + + case 2: + default: + extra = ExtraBlbits; + break; + }; var base_Renamed = this.StatDesc.ExtraBase; var max_length = this.StatDesc.MaxLength; int h; // heap index @@ -313,7 +340,22 @@ internal void Gen_bitlen(Deflate s) internal void Build_tree(Deflate s) { var tree = this.DynTree; - var stree = this.StatDesc.StaticTreeValue; + ReadOnlySpan stree; + switch (this.StatDesc.StaticTreeOption) + { + case 0: + stree = StaticTree.StaticLtree; + break; + + case 1: + stree = StaticTree.StaticDtree; + break; + + case 2: + default: + stree = null; + break; + }; var elems = this.StatDesc.Elems; int n, m; // iterate over heap elements var max_code = -1; // largest code with non zero frequency diff --git a/Zlib.Managed/ComponentAce/ZOutputStream.cs b/Fuyu.Compression/Elskom/ZOutputStream.cs similarity index 96% rename from Zlib.Managed/ComponentAce/ZOutputStream.cs rename to Fuyu.Compression/Elskom/ZOutputStream.cs index 4020a826..deac1700 100644 --- a/Zlib.Managed/ComponentAce/ZOutputStream.cs +++ b/Fuyu.Compression/Elskom/ZOutputStream.cs @@ -1,16 +1,10 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Fuyu.Compression; -using Elskom.Generic.Libs; - -namespace ComponentAce.Compression.Libs.zlib +namespace Elskom.Generic.Libs { - using System; - using System.Diagnostics.CodeAnalysis; - using System.IO; - /// /// Class that provices a zlib output stream that supports /// compression and decompression. @@ -34,7 +28,7 @@ public ZOutputStream(Stream output) /// /// The output stream. /// The compression level for the data to compress. - public ZOutputStream(Stream output, int level) + public ZOutputStream(Stream output, CompressionLevel level) { this.BaseStream = output; this.InitBlock(); diff --git a/Zlib.Managed/Elskom/ZStream.cs b/Fuyu.Compression/Elskom/ZStream.cs similarity index 91% rename from Zlib.Managed/Elskom/ZStream.cs rename to Fuyu.Compression/Elskom/ZStream.cs index 5e808165..54d74639 100644 --- a/Zlib.Managed/Elskom/ZStream.cs +++ b/Fuyu.Compression/Elskom/ZStream.cs @@ -1,16 +1,12 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. +using System; +using Fuyu.Compression; namespace Elskom.Generic.Libs { - using System; - /// /// The zlib stream class. /// - internal sealed class ZStream + public sealed class ZStream { private const int MAXWBITS = 15; // 32K LZ77 window @@ -149,14 +145,14 @@ internal int InflateEnd() /// The dictionary to use. /// The dictionary length. /// The zlib status state. - internal int InflateSetDictionary(byte[] dictionary, int dictLength) => this.Istate == null ? ZSTREAMERROR : Libs.Inflate.InflateSetDictionary(this, dictionary, dictLength); + internal int InflateSetDictionary(Span dictionary, int dictLength) => this.Istate == null ? ZSTREAMERROR : Libs.Inflate.InflateSetDictionary(this, dictionary, dictLength); /// /// Initializes compression. /// /// The compression level to use. /// The zlib status state. - internal int DeflateInit(int level) => this.DeflateInit(level, MAXWBITS); + internal int DeflateInit(CompressionLevel level) => this.DeflateInit(level, MAXWBITS); /// /// Initializes compression. @@ -164,7 +160,7 @@ internal int InflateEnd() /// The compression level to use. /// The window bits to use. /// The zlib status state. - internal int DeflateInit(int level, int bits) + internal int DeflateInit(CompressionLevel level, int bits) { this.Dstate = new Deflate(); return this.Dstate.DeflateInit(this, level, bits); @@ -199,7 +195,7 @@ internal int DeflateEnd() /// The compression level to use. /// The strategy to use for compression. /// The zlib status state. - internal int DeflateParams(int level, int strategy) => this.Dstate == null ? ZSTREAMERROR : this.Dstate.DeflateParams(this, level, strategy); + internal int DeflateParams(CompressionLevel level, CompressionStrategy strategy) => this.Dstate == null ? ZSTREAMERROR : this.Dstate.DeflateParams(this, level, strategy); /// /// Sets the deflate dictionary. @@ -207,7 +203,7 @@ internal int DeflateEnd() /// The dictionary to use. /// The dictionary length. /// The zlib status state. - internal int DeflateSetDictionary(byte[] dictionary, int dictLength) => this.Dstate == null ? ZSTREAMERROR : this.Dstate.DeflateSetDictionary(this, dictionary, dictLength); + internal int DeflateSetDictionary(ReadOnlySpan dictionary, int dictLength) => this.Dstate == null ? ZSTREAMERROR : this.Dstate.DeflateSetDictionary(this, dictionary.ToArray(), dictLength); /// /// Frees everything. diff --git a/Zlib.Managed/Elskom/ZStreamException.cs b/Fuyu.Compression/Elskom/ZStreamException.cs similarity index 90% rename from Zlib.Managed/Elskom/ZStreamException.cs rename to Fuyu.Compression/Elskom/ZStreamException.cs index b61ba747..d033f644 100644 --- a/Zlib.Managed/Elskom/ZStreamException.cs +++ b/Fuyu.Compression/Elskom/ZStreamException.cs @@ -1,13 +1,8 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. +using System; +using System.IO; namespace Elskom.Generic.Libs { - using System; - using System.IO; - /// /// The exception that is thrown when an zlib error occurs. /// diff --git a/Zlib.Managed/Elskom/ZlibConst.cs b/Fuyu.Compression/Elskom/ZlibConst.cs similarity index 73% rename from Zlib.Managed/Elskom/ZlibConst.cs rename to Fuyu.Compression/Elskom/ZlibConst.cs index fa689c2d..a8e42095 100644 --- a/Zlib.Managed/Elskom/ZlibConst.cs +++ b/Fuyu.Compression/Elskom/ZlibConst.cs @@ -1,8 +1,3 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. - namespace Elskom.Generic.Libs { /// @@ -32,23 +27,6 @@ internal static class ZlibConst /// internal const int ZDEFAULTCOMPRESSION = -1; - // compression strategy - - /// - /// Filtered compression strategy. - /// - internal const int ZFILTERED = 1; - - /// - /// huffman compression strategy. - /// - internal const int ZHUFFMANONLY = 2; - - /// - /// The default compression strategy. - /// - internal const int ZDEFAULTSTRATEGY = 0; - /// /// No flush. /// @@ -118,11 +96,5 @@ internal static class ZlibConst /// Zlib version error. /// internal const int ZVERSIONERROR = -6; - - /// - /// Gets the version to zlib.net. - /// - /// The version string to this version of zlib.net. - internal static string Version() => typeof(ZlibConst).Assembly.GetName().Version.ToString(3); } } \ No newline at end of file diff --git a/Zlib.Managed/Zlib.Managed.csproj b/Fuyu.Compression/Fuyu.Compression.csproj similarity index 51% rename from Zlib.Managed/Zlib.Managed.csproj rename to Fuyu.Compression/Fuyu.Compression.csproj index 749c3c7d..e500049a 100644 --- a/Zlib.Managed/Zlib.Managed.csproj +++ b/Fuyu.Compression/Fuyu.Compression.csproj @@ -1,12 +1,7 @@ - netstandard2.0 - - - - true - Elskom.snk + net8.0;netstandard2.0 @@ -14,4 +9,8 @@ 1.0.3.0 + + + + \ No newline at end of file diff --git a/Zlib.Managed/LICENSE b/Fuyu.Compression/LICENSE similarity index 95% rename from Zlib.Managed/LICENSE rename to Fuyu.Compression/LICENSE index 1c904c01..5348ee33 100644 --- a/Zlib.Managed/LICENSE +++ b/Fuyu.Compression/LICENSE @@ -1,3 +1,9 @@ +Copyright (c) 2018-2020, Els_kom org. +https://github.com/Elskom/ +All rights reserved. + +-------------------------------------------------------------------------------- + Copyright (c) 2006, ComponentAce http://www.componentace.com All rights reserved. diff --git a/Fuyu.Common/Compression/Zlib.cs b/Fuyu.Compression/MemoryZlib.cs similarity index 84% rename from Fuyu.Common/Compression/Zlib.cs rename to Fuyu.Compression/MemoryZlib.cs index f94828fb..23bd2e10 100644 --- a/Fuyu.Common/Compression/Zlib.cs +++ b/Fuyu.Compression/MemoryZlib.cs @@ -4,11 +4,11 @@ using System; using System.IO; -using ComponentAce.Compression.Libs.zlib; +using Elskom.Generic.Libs; -namespace Fuyu.Common.Compression +namespace Fuyu.Compression { - public static class Zlib + public static class MemoryZlib { public static bool IsCompressed(byte[] data) { @@ -37,16 +37,16 @@ public static bool IsCompressed(byte[] data) } } - public static byte[] Compress(byte[] data, ZlibCompression level) + public static byte[] Compress(byte[] data, CompressionLevel level) { - if (level == ZlibCompression.NoCompression) + if (level == CompressionLevel.NoCompression) { throw new ArgumentException("level cannot be ZlibCompression.NoCompression"); } using (var ms = new MemoryStream()) { - using (var zs = new ZOutputStream(ms, (int)level)) + using (var zs = new ZOutputStream(ms, level)) { zs.Write(data, 0, data.Length); } diff --git a/Fuyu.Compression/README.md b/Fuyu.Compression/README.md new file mode 100644 index 00000000..4118618b --- /dev/null +++ b/Fuyu.Compression/README.md @@ -0,0 +1,23 @@ +# Fuyu.Compression + +Reimplementing `bsg.componentace.compression.libs.zlib` on top of `Elskom/Zlib.Managed` with additional changes. + +- Based on release [1.1.4.0](https://github.com/Elskom/zlib.managed/releases/tag/1.1.4.0) +- Cherrypicked changes from [`main`](https://github.com/Elskom/zlib.managed) + +## Notes + +Since I wanted to make it directly compatible with EFT, I decided to include EFT's `SimpleZlib`'s signatures with +bindings to `MemoeryZlib`. This assumes that only `SimpleZlib` needs to be exposed to the client in order to function +as intended. You can use this library to link against instead of EFT's binary. + +Please note it is impossible to run the libraries alongside eachother when `SimpleZlib` is imported from the namespace. +You must divide the codebase in two projects; one targeting EFT's binary, the other targeting this project. +Alternatively use `MemoryZlib` instead. + +## Changes + +- Uses EFT assembly info +- Made all classes inside `Elskom/` folder `internal` except `ZStream` +- Uses `ReadOnlySpan`, `Span` +- Improved code clarity diff --git a/Fuyu.Tests.Compression/Fuyu.Tests.Compression.csproj b/Fuyu.Tests.Compression/Fuyu.Tests.Compression.csproj new file mode 100644 index 00000000..bbb21389 --- /dev/null +++ b/Fuyu.Tests.Compression/Fuyu.Tests.Compression.csproj @@ -0,0 +1,21 @@ + + + + net8.0 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fuyu.Tests.Compression/TestData.cs b/Fuyu.Tests.Compression/TestData.cs new file mode 100644 index 00000000..22440c8e --- /dev/null +++ b/Fuyu.Tests.Compression/TestData.cs @@ -0,0 +1,129 @@ +using System.Collections.Generic; +using System.Text; +using Fuyu.Compression; + +namespace Fuyu.Tests.Compression +{ + class TestData + { + private const string _text = "The quick brown lazy fox jumps over the lazy dog"; + internal static readonly byte[] Inflated; + internal static readonly Dictionary Deflated; + + static TestData() + { + Inflated = Encoding.UTF8.GetBytes(_text); + Deflated = new Dictionary + { + { + CompressionLevel.Level1, + new byte[] + { + 0x78, 0x01, 0x0B, 0xC9, 0x48, 0x55, 0x28, 0x2C, 0xCD, + 0x4C, 0xCE, 0x56, 0x48, 0x2A, 0xCA, 0x2F, 0xCF, 0x53, + 0xC8, 0x49, 0xAC, 0xAA, 0x54, 0x48, 0xCB, 0xAF, 0x50, + 0xC8, 0x2A, 0xCD, 0x2D, 0x28, 0x56, 0xC8, 0x2F, 0x4B, + 0x2D, 0x52, 0x28, 0x01, 0xAA, 0x01, 0x8B, 0xA7, 0xE4, + 0xA7, 0x03, 0x00, 0xB1, 0x7F, 0x11, 0xBA + } + }, + { + CompressionLevel.Level2, + new byte[] + { + 0x78, 0x01, 0x0B, 0xC9, 0x48, 0x55, 0x28, 0x2C, 0xCD, + 0x4C, 0xCE, 0x56, 0x48, 0x2A, 0xCA, 0x2F, 0xCF, 0x53, + 0xC8, 0x49, 0xAC, 0xAA, 0x54, 0x48, 0xCB, 0xAF, 0x50, + 0xC8, 0x2A, 0xCD, 0x2D, 0x28, 0x56, 0xC8, 0x2F, 0x4B, + 0x2D, 0x52, 0x28, 0x01, 0xAA, 0x01, 0x8B, 0xA7, 0xE4, + 0xA7, 0x03, 0x00, 0xB1, 0x7F, 0x11, 0xBA + } + }, + { + CompressionLevel.Level3, + new byte[] + { + 0x78, 0x5E, 0x0B, 0xC9, 0x48, 0x55, 0x28, 0x2C, 0xCD, + 0x4C, 0xCE, 0x56, 0x48, 0x2A, 0xCA, 0x2F, 0xCF, 0x53, + 0xC8, 0x49, 0xAC, 0xAA, 0x54, 0x48, 0xCB, 0xAF, 0x50, + 0xC8, 0x2A, 0xCD, 0x2D, 0x28, 0x56, 0xC8, 0x2F, 0x4B, + 0x2D, 0x52, 0x28, 0x01, 0xAA, 0x01, 0x8B, 0xA7, 0xE4, + 0xA7, 0x03, 0x00, 0xB1, 0x7F, 0x11, 0xBA + } + }, + { + CompressionLevel.Level4, + new byte[] + { + 0x78, 0x5E, 0x0B, 0xC9, 0x48, 0x55, 0x28, 0x2C, 0xCD, + 0x4C, 0xCE, 0x56, 0x48, 0x2A, 0xCA, 0x2F, 0xCF, 0x53, + 0xC8, 0x49, 0xAC, 0xAA, 0x54, 0x48, 0xCB, 0xAF, 0x50, + 0xC8, 0x2A, 0xCD, 0x2D, 0x28, 0x56, 0xC8, 0x2F, 0x4B, + 0x2D, 0x52, 0x28, 0x01, 0xAA, 0x01, 0x8B, 0xA7, 0xE4, + 0xA7, 0x03, 0x00, 0xB1, 0x7F, 0x11, 0xBA + } + }, + { + CompressionLevel.Level5, + new byte[] + { + 0x78, 0x9C, 0x0B, 0xC9, 0x48, 0x55, 0x28, 0x2C, 0xCD, + 0x4C, 0xCE, 0x56, 0x48, 0x2A, 0xCA, 0x2F, 0xCF, 0x53, + 0xC8, 0x49, 0xAC, 0xAA, 0x54, 0x48, 0xCB, 0xAF, 0x50, + 0xC8, 0x2A, 0xCD, 0x2D, 0x28, 0x56, 0xC8, 0x2F, 0x4B, + 0x2D, 0x52, 0x28, 0x01, 0xAA, 0x01, 0x8B, 0xA7, 0xE4, + 0xA7, 0x03, 0x00, 0xB1, 0x7F, 0x11, 0xBA + } + }, + { + CompressionLevel.Level6, + new byte[] + { + 0x78, 0x9C, 0x0B, 0xC9, 0x48, 0x55, 0x28, 0x2C, 0xCD, + 0x4C, 0xCE, 0x56, 0x48, 0x2A, 0xCA, 0x2F, 0xCF, 0x53, + 0xC8, 0x49, 0xAC, 0xAA, 0x54, 0x48, 0xCB, 0xAF, 0x50, + 0xC8, 0x2A, 0xCD, 0x2D, 0x28, 0x56, 0xC8, 0x2F, 0x4B, + 0x2D, 0x52, 0x28, 0x01, 0xAA, 0x01, 0x8B, 0xA7, 0xE4, + 0xA7, 0x03, 0x00, 0xB1, 0x7F, 0x11, 0xBA + } + }, + { + CompressionLevel.Level7, + new byte[] + { + 0x78, 0xDA, 0x0B, 0xC9, 0x48, 0x55, 0x28, 0x2C, 0xCD, + 0x4C, 0xCE, 0x56, 0x48, 0x2A, 0xCA, 0x2F, 0xCF, 0x53, + 0xC8, 0x49, 0xAC, 0xAA, 0x54, 0x48, 0xCB, 0xAF, 0x50, + 0xC8, 0x2A, 0xCD, 0x2D, 0x28, 0x56, 0xC8, 0x2F, 0x4B, + 0x2D, 0x52, 0x28, 0x01, 0xAA, 0x01, 0x8B, 0xA7, 0xE4, + 0xA7, 0x03, 0x00, 0xB1, 0x7F, 0x11, 0xBA + } + }, + { + CompressionLevel.Level8, + new byte[] + { + 0x78, 0xDA, 0x0B, 0xC9, 0x48, 0x55, 0x28, 0x2C, 0xCD, + 0x4C, 0xCE, 0x56, 0x48, 0x2A, 0xCA, 0x2F, 0xCF, 0x53, + 0xC8, 0x49, 0xAC, 0xAA, 0x54, 0x48, 0xCB, 0xAF, 0x50, + 0xC8, 0x2A, 0xCD, 0x2D, 0x28, 0x56, 0xC8, 0x2F, 0x4B, + 0x2D, 0x52, 0x28, 0x01, 0xAA, 0x01, 0x8B, 0xA7, 0xE4, + 0xA7, 0x03, 0x00, 0xB1, 0x7F, 0x11, 0xBA + } + }, + { + CompressionLevel.Level9, + new byte[] + { + 0x78, 0xDA, 0x0B, 0xC9, 0x48, 0x55, 0x28, 0x2C, 0xCD, + 0x4C, 0xCE, 0x56, 0x48, 0x2A, 0xCA, 0x2F, 0xCF, 0x53, + 0xC8, 0x49, 0xAC, 0xAA, 0x54, 0x48, 0xCB, 0xAF, 0x50, + 0xC8, 0x2A, 0xCD, 0x2D, 0x28, 0x56, 0xC8, 0x2F, 0x4B, + 0x2D, 0x52, 0x28, 0x01, 0xAA, 0x01, 0x8B, 0xA7, 0xE4, + 0xA7, 0x03, 0x00, 0xB1, 0x7F, 0x11, 0xBA + } + } + }; + } + } +} \ No newline at end of file diff --git a/Fuyu.Tests.Compression/Units/MemoryZlibTest.cs b/Fuyu.Tests.Compression/Units/MemoryZlibTest.cs new file mode 100644 index 00000000..dd5e96ad --- /dev/null +++ b/Fuyu.Tests.Compression/Units/MemoryZlibTest.cs @@ -0,0 +1,83 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Fuyu.Compression; +using Fuyu.Tests.Compression; + +namespace Fuyu.Tests.Compression.Units +{ + [TestClass] + public class MemoryZlibTest + { + [TestMethod] + [DataRow(CompressionLevel.Level1)] + [DataRow(CompressionLevel.Level2)] + [DataRow(CompressionLevel.Level3)] + [DataRow(CompressionLevel.Level4)] + [DataRow(CompressionLevel.Level5)] + [DataRow(CompressionLevel.Level6)] + [DataRow(CompressionLevel.Level7)] + [DataRow(CompressionLevel.Level8)] + [DataRow(CompressionLevel.Level9)] + public void TestIsCompressed(CompressionLevel level) + { + var data = TestData.Deflated[level]; + + if (!MemoryZlib.IsCompressed(data)) + { + Assert.Fail($"Level {level}"); + } + } + + [TestMethod] + [DataRow(CompressionLevel.Level1)] + [DataRow(CompressionLevel.Level2)] + [DataRow(CompressionLevel.Level3)] + [DataRow(CompressionLevel.Level4)] + [DataRow(CompressionLevel.Level5)] + [DataRow(CompressionLevel.Level6)] + [DataRow(CompressionLevel.Level7)] + [DataRow(CompressionLevel.Level8)] + [DataRow(CompressionLevel.Level9)] + public void TestDeflate(CompressionLevel level) + { + // compress + var result = MemoryZlib.Compress(TestData.Inflated, level); + + // validate + var data = TestData.Deflated[level]; + + if (!result.SequenceEqual(data)) + { + Assert.Fail($"Level {level}"); + } + } + + [TestMethod] + [DataRow(CompressionLevel.Level1)] + [DataRow(CompressionLevel.Level2)] + [DataRow(CompressionLevel.Level3)] + [DataRow(CompressionLevel.Level4)] + [DataRow(CompressionLevel.Level5)] + [DataRow(CompressionLevel.Level6)] + [DataRow(CompressionLevel.Level7)] + [DataRow(CompressionLevel.Level8)] + [DataRow(CompressionLevel.Level9)] + public void TestInflate(CompressionLevel level) + { + var data = TestData.Deflated[level]; + + // decompress + var result = MemoryZlib.Decompress(data); + + // validate + if (!result.SequenceEqual(TestData.Inflated)) + { + Assert.Fail($"Level {level}"); + } + } + } +} \ No newline at end of file diff --git a/Fuyu.sln b/Fuyu.sln index ef768a4e..499ad3ce 100644 --- a/Fuyu.sln +++ b/Fuyu.sln @@ -35,7 +35,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fuyu.Tests.Backend.Core", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fuyu.Tests.Backend.EFT", "Fuyu.Tests.Backend.EFT\Fuyu.Tests.Backend.EFT.csproj", "{BEC4515F-F90C-4055-9D26-0000408E516E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zlib.Managed", "Zlib.Managed\Zlib.Managed.csproj", "{AFCA8CD1-4DFC-4D64-8DC7-872000BEE1EA}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fuyu.Compression", "Fuyu.Compression\Fuyu.Compression.csproj", "{AFCA8CD1-4DFC-4D64-8DC7-872000BEE1EA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fuyu.Tests.Compression", "Fuyu.Tests.Compression\Fuyu.Tests.Compression.csproj", "{7756BCDD-7A6D-46C9-9AF2-05C934C96B93}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -114,5 +116,9 @@ Global {AFCA8CD1-4DFC-4D64-8DC7-872000BEE1EA}.Debug|Any CPU.Build.0 = Debug|Any CPU {AFCA8CD1-4DFC-4D64-8DC7-872000BEE1EA}.Release|Any CPU.ActiveCfg = Release|Any CPU {AFCA8CD1-4DFC-4D64-8DC7-872000BEE1EA}.Release|Any CPU.Build.0 = Release|Any CPU + {7756BCDD-7A6D-46C9-9AF2-05C934C96B93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7756BCDD-7A6D-46C9-9AF2-05C934C96B93}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7756BCDD-7A6D-46C9-9AF2-05C934C96B93}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7756BCDD-7A6D-46C9-9AF2-05C934C96B93}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Zlib.Managed/Elskom.snk b/Zlib.Managed/Elskom.snk deleted file mode 100644 index 19c0650a..00000000 Binary files a/Zlib.Managed/Elskom.snk and /dev/null differ diff --git a/Zlib.Managed/Elskom/Adler32.cs b/Zlib.Managed/Elskom/Adler32.cs deleted file mode 100644 index 8b9b78d5..00000000 --- a/Zlib.Managed/Elskom/Adler32.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. - -namespace Elskom.Generic.Libs -{ - internal static class Adler32 - { - // largest prime smaller than 65536 - private const int BASE = 65521; - - // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 - private const int NMAX = 5552; - - internal static long Calculate(long adler, byte[] buf, int index, int len) - { - if (buf == null) - { - return 1L; - } - - var s1 = adler & 0xffff; - var s2 = (adler >> 16) & 0xffff; - int k; - - while (len > 0) - { - k = len < NMAX ? len : NMAX; - len -= k; - while (k >= 16) - { - s1 += buf[index++] & 0xff; - s2 += s1; - s1 += buf[index++] & 0xff; - s2 += s1; - s1 += buf[index++] & 0xff; - s2 += s1; - s1 += buf[index++] & 0xff; - s2 += s1; - s1 += buf[index++] & 0xff; - s2 += s1; - s1 += buf[index++] & 0xff; - s2 += s1; - s1 += buf[index++] & 0xff; - s2 += s1; - s1 += buf[index++] & 0xff; - s2 += s1; - s1 += buf[index++] & 0xff; - s2 += s1; - s1 += buf[index++] & 0xff; - s2 += s1; - s1 += buf[index++] & 0xff; - s2 += s1; - s1 += buf[index++] & 0xff; - s2 += s1; - s1 += buf[index++] & 0xff; - s2 += s1; - s1 += buf[index++] & 0xff; - s2 += s1; - s1 += buf[index++] & 0xff; - s2 += s1; - s1 += buf[index++] & 0xff; - s2 += s1; - k -= 16; - } - - if (k != 0) - { - do - { - s1 += buf[index++] & 0xff; - s2 += s1; - } - while (--k != 0); - } - - s1 %= BASE; - s2 %= BASE; - } - - return (s2 << 16) | s1; - } - } -} \ No newline at end of file diff --git a/Zlib.Managed/Elskom/InflaterInputBuffer.cs b/Zlib.Managed/Elskom/InflaterInputBuffer.cs deleted file mode 100644 index d8739e35..00000000 --- a/Zlib.Managed/Elskom/InflaterInputBuffer.cs +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. - -namespace Elskom.Generic.Libs -{ - /* - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - - /// - /// An input buffer customised for use by . - /// - /// - /// The buffer supports decryption of incoming data. - /// - internal class InflaterInputBuffer - { - private readonly Stream inputStream; - - /// - /// Initializes a new instance of the class with a default buffer size. - /// - /// The stream to buffer. - internal InflaterInputBuffer(Stream stream) - : this(stream, 4096) - { - } - - /// - /// Initializes a new instance of the class with a custom buffer size. - /// - /// The stream to buffer. - /// The size to use for the buffer. - /// A minimum buffer size of 1KB is permitted. Lower sizes are treated as 1KB. - internal InflaterInputBuffer(Stream stream, int bufferSize) - { - this.inputStream = stream; - if (bufferSize < 1024) - { - bufferSize = 1024; - } - - this.RawData = new byte[bufferSize]; - this.ClearText = this.RawData; - } - - /// - /// Gets the length of bytes bytes in the . - /// - internal int RawLength { get; private set; } - - /// - /// Gets the contents of the raw data buffer. - /// - /// This may contain encrypted data. - internal IEnumerable RawData { get; private set; } - - /// - /// Gets the number of useable bytes in . - /// - internal int ClearTextLength { get; private set; } - - /// - /// Gets the contents of the clear text buffer. - /// - internal IEnumerable ClearText { get; private set; } - - /// - /// Gets or Sets the number of bytes available. - /// - internal int Available { get; set; } - - /// - /// Call passing the current clear text buffer contents. - /// - /// - /// When is . - /// - /// The stream for which to call Read. - internal void SetInflaterInput(ZInputStream zinput) - { - if (zinput == null) - { - throw new ArgumentNullException(nameof(zinput)); - } - - if (this.Available > 0) - { - // I think this should read. - _ = zinput.Read(this.ClearText.ToArray(), this.ClearTextLength - this.Available, this.Available); - - // .SetInput(this.ClearText, this.ClearTextLength - this.Available, this.Available); - this.Available = 0; - } - } - - /// - /// Fill the buffer from the underlying input stream. - /// - internal void Fill() - { - this.RawLength = 0; - var toRead = this.RawData.ToArray().Length; - - while (toRead > 0) - { - var count = this.inputStream.Read(this.RawData.ToArray(), this.RawLength, toRead); - if (count <= 0) - { - break; - } - - this.RawLength += count; - toRead -= count; - } - - this.ClearTextLength = this.RawLength; - - this.Available = this.ClearTextLength; - } - - /// - /// Read a buffer directly from the input stream. - /// - /// The buffer to fill. - /// Returns the number of bytes read. - internal int ReadRawBuffer(byte[] buffer) - => this.ReadRawBuffer(buffer, 0, buffer.Length); - - /// - /// Read a buffer directly from the input stream. - /// - /// The buffer to read into. - /// The offset to start reading data into. - /// The number of bytes to read. - /// Returns the number of bytes read. - internal int ReadRawBuffer(byte[] outBuffer, int offset, int length) - { - if (length < 0) - { - throw new ArgumentOutOfRangeException(nameof(length)); - } - - var currentOffset = offset; - var currentLength = length; - - while (currentLength > 0) - { - if (this.Available <= 0) - { - this.Fill(); - if (this.Available <= 0) - { - return 0; - } - } - - var toCopy = Math.Min(currentLength, this.Available); - Array.Copy(this.RawData.ToArray(), this.RawLength - this.Available, outBuffer, currentOffset, toCopy); - currentOffset += toCopy; - currentLength -= toCopy; - this.Available -= toCopy; - } - - return length; - } - - /// - /// Read clear text data from the input stream. - /// - /// The buffer to add data to. - /// The offset to start adding data at. - /// The number of bytes to read. - /// Returns the number of bytes actually read. - internal int ReadClearTextBuffer(byte[] outBuffer, int offset, int length) - { - if (length < 0) - { - throw new ArgumentOutOfRangeException(nameof(length)); - } - - var currentOffset = offset; - var currentLength = length; - - while (currentLength > 0) - { - if (this.Available <= 0) - { - this.Fill(); - if (this.Available <= 0) - { - return 0; - } - } - - var toCopy = Math.Min(currentLength, this.Available); - Array.Copy(this.ClearText.ToArray(), this.ClearTextLength - this.Available, outBuffer, currentOffset, toCopy); - currentOffset += toCopy; - currentLength -= toCopy; - this.Available -= toCopy; - } - - return length; - } - - /// - /// Read a from the input stream. - /// - /// Returns the byte read. - internal int ReadLeByte() - { - if (this.Available <= 0) - { - this.Fill(); - if (this.Available <= 0) - { - throw new Exception("EOF in header"); - } - } - - var result = this.RawData.ToArray()[this.RawLength - this.Available]; - this.Available -= 1; - return result; - } - - /// - /// Read an in little endian byte order. - /// - /// The short value read case to an int. - internal int ReadLeShort() - => this.ReadLeByte() | (this.ReadLeByte() << 8); - - /// - /// Read an in little endian byte order. - /// - /// The int value read. - internal int ReadLeInt() - => this.ReadLeShort() | (this.ReadLeShort() << 16); - - /// - /// Read a in little endian byte order. - /// - /// The long value read. - internal long ReadLeLong() - => (uint)this.ReadLeInt() | ((long)this.ReadLeInt() << 32); - } - */ -} diff --git a/Zlib.Managed/Elskom/NotPackableException.cs b/Zlib.Managed/Elskom/NotPackableException.cs deleted file mode 100644 index 16614b30..00000000 --- a/Zlib.Managed/Elskom/NotPackableException.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. - -namespace Elskom.Generic.Libs -{ - using System; - using System.IO; - - /// - /// Zlib Memory Packing failure error. - /// - [Serializable] - internal sealed class NotPackableException : IOException - { - /// - /// Initializes a new instance of the class. - /// - internal NotPackableException() - : base() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The error string. - internal NotPackableException(string s) - : base(s) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The error string. - /// The Exception that caused this Exception. - internal NotPackableException(string s, Exception ex) - : base(s, ex) - { - } - } -} diff --git a/Zlib.Managed/Elskom/NotUnpackableException.cs b/Zlib.Managed/Elskom/NotUnpackableException.cs deleted file mode 100644 index 1b793fab..00000000 --- a/Zlib.Managed/Elskom/NotUnpackableException.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. - -namespace Elskom.Generic.Libs -{ - using System; - using System.IO; - - /// - /// Zlib Memory Unpacking failure error. - /// - [Serializable] - internal sealed class NotUnpackableException : IOException - { - /// - /// Initializes a new instance of the class with no argrument. - /// - internal NotUnpackableException() - : base() - { - } - - /// - /// Initializes a new instance of the class with an string argrument. - /// - /// The error string. - internal NotUnpackableException(string s) - : base(s) - { - } - - /// - /// Initializes a new instance of the class with an string argrument - /// and the exception that cuased this exception. - /// - /// The error string. - /// The Exception that caused this Exception. - internal NotUnpackableException(string s, Exception ex) - : base(s, ex) - { - } - } -} diff --git a/Zlib.Managed/Elskom/ZInputStream.cs b/Zlib.Managed/Elskom/ZInputStream.cs deleted file mode 100644 index 40cdccb7..00000000 --- a/Zlib.Managed/Elskom/ZInputStream.cs +++ /dev/null @@ -1,302 +0,0 @@ -// Copyright (c) 2018-2020, Els_kom org. -// https://github.com/Elskom/ -// All rights reserved. -// license: see LICENSE for more details. - -namespace Elskom.Generic.Libs -{ - using System; - using System.Diagnostics.CodeAnalysis; - using System.IO; - - /// - /// Class that provices a zlib input stream that supports - /// compression and decompression. - /// - internal class ZInputStream : Stream - { - /// - /// Initializes a new instance of the class. - /// - /// The input stream. - internal ZInputStream(Stream input) - { - this.BaseStream = input; - this.InitBlock(); - this.Z.InflateInit(); - this.Compress = false; - this.Z.NextIn = this.Buf; - this.Z.NextInIndex = 0; - this.Z.AvailIn = 0; - } - - /// - /// Initializes a new instance of the class. - /// - /// The input stream. - /// The compression level for the data to compress. - internal ZInputStream(Stream input, int level) - { - this.BaseStream = input; - this.InitBlock(); - this.Z.DeflateInit(level); - this.Compress = true; - this.Z.NextIn = this.Buf; - this.Z.NextInIndex = 0; - this.Z.AvailIn = 0; - } - - /// - /// Gets the base stream that this stream contains. - /// - internal Stream BaseStream { get; private set; } - - /// - /// Gets the base zlib stream. - /// - internal ZStream Z { get; private set; } = new ZStream(); - - /// - /// Gets or sets the flush mode for this stream. - /// - internal virtual int FlushMode { get; set; } - - /// - /// Gets the total number of bytes input so far. - /// - internal virtual long TotalIn => this.Z.TotalIn; - - /// - /// Gets the total number of bytes output so far. - /// - internal virtual long TotalOut => this.Z.TotalOut; - - /// - /// Gets or sets a value indicating whether there is more input. - /// - internal bool Moreinput { get; set; } - - /// - /// Gets a value indicating whether the stream is finished. - /// - internal bool IsFinished { get; private set; } - - /// - public override bool CanRead => true; - - /// - public override bool CanSeek => false; - - /// - public override bool CanWrite => false; - - /// - public override long Length => this.BaseStream.Length; - - /// - public override long Position - { - get - { - return this.BaseStream.Position; - } - set - { - this.BaseStream.Position = value; - } - } - - /// - /// Gets the stream's buffer size. - /// - protected int Bufsize { get; private set; } = 512; - - /// - /// Gets the stream's buffer. - /// - protected byte[] Buf { get; private set; } - - /// - /// Gets the stream's single byte buffer value. - /// For reading 1 byte at a time. - /// - protected byte[] Buf1 { get; private set; } = new byte[1]; - - /// - /// Gets a value indicating whether this stream is setup for compression. - /// - protected bool Compress { get; private set; } - - /// - public override int ReadByte() => this.Read(this.Buf1, 0, 1) == -1 ? -1 : this.Buf1[0] & 0xFF; - - /// - public override int Read(byte[] b, int off, int len) - { - if (len == 0) - { - return 0; - } - - int err; - this.Z.NextOut = b; - this.Z.NextOutIndex = off; - this.Z.AvailOut = len; - do - { - if ((this.Z.AvailIn == 0) && (!this.Moreinput)) - { - // if buffer is empty and more input is avaiable, refill it - this.Z.NextInIndex = 0; - this.Z.AvailIn = SupportClass.ReadInput(this.BaseStream, this.Buf, 0, this.Bufsize); // (bufsize - /// Skips a certin amount of data. - /// - /// The amount to skip. - /// - /// less than or equal to count depending on the data available - /// in the source Stream or -1 if the end of the stream is - /// reached. - /// - internal long Skip(long n) - { - var len = 512; - if (n < len) - { - len = (int)n; - } - - var tmp = new byte[len]; - return SupportClass.ReadInput(this.BaseStream, tmp, 0, tmp.Length); - } - - /// - /// Finishes the stream. - /// - internal virtual void Finish() - { - if (!this.IsFinished) - { - int err; - do - { - this.Z.NextOut = this.Buf; - this.Z.NextOutIndex = 0; - this.Z.AvailOut = this.Bufsize; - err = this.Compress ? this.Z.Deflate(ZlibConst.ZFINISH) : this.Z.Inflate(ZlibConst.ZFINISH); - - if (err != ZlibConst.ZSTREAMEND && err != ZlibConst.ZOK) - { - throw new ZStreamException((this.Compress ? "de" : "in") + "flating: " + this.Z.Msg); - } - - if (this.Bufsize - this.Z.AvailOut > 0) - { - this.BaseStream.Write(this.Buf, 0, this.Bufsize - this.Z.AvailOut); - } - - if (err == ZlibConst.ZSTREAMEND) - { - break; - } - } - while (this.Z.AvailIn > 0 || this.Z.AvailOut == 0); - - this.IsFinished = true; - } - } - - /// - /// Ends the compression or decompression on the stream. - /// - internal virtual void EndStream() - { - if (this.Compress) - { - this.Z.DeflateEnd(); - } - else - { - this.Z.InflateEnd(); - } - - this.Z.Free(); - this.Z = null; - } - - /// - [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "This method should not throw any exceptions.")] - public override void Close() - { - try - { - try - { - this.Finish(); - } - catch (Exception) - { - } - } - finally - { - this.EndStream(); - this.BaseStream.Close(); - } - } - - /// - public override void Flush() => this.BaseStream.Flush(); - - /// - public override long Seek(long offset, SeekOrigin origin) => 0; - - /// - public override void SetLength(long value) - { - throw new NotImplementedException(); - } - - /// - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotImplementedException(); - } - - private void InitBlock() - { - this.FlushMode = ZlibConst.ZNOFLUSH; - this.Buf = new byte[this.Bufsize]; - } - } -} diff --git a/Zlib.Managed/README.md b/Zlib.Managed/README.md deleted file mode 100644 index 0919e6c9..00000000 --- a/Zlib.Managed/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Zlib.Managed - -A more recent version of `ComponentAce.Compression.Libs.Zlib` with tweaks - -- Based on release [1.1.4.0](https://github.com/Elskom/zlib.managed/releases/tag/1.1.4.0) -- Made all classes inside `Elskom/` folder `internal` -- Uses EFT assembly info - -## Notes - -Since `ZOutputStream` exists in EFT's `bsg.componentace.compression.libs.zlib`, -I want to avoid loading two zlib libraries. That's why `ZOutputStream` only -exposes calls used by `Fuyu.Platform.Common.Compression`, the code inside the -`Elskom` folder is marked `internal` and the assembly matches EFT's assembly -info. - -This way you don't need to distribute this library when using `Fuyu` lib inside -the client. \ No newline at end of file