/// <summary>
        /// Decompresses the specified bytes using LZMA and the
        /// <see cref="CompressionSettings"/> that have been used to originally compress the bytes.
        /// </summary>
        /// <param name="compressedBytes">The compressed <c>byte[]</c> array that you want to decompress.</param>
        /// <param name="compressionSettings">The <see cref="CompressionSettings"/> that have been used to compress the bytes.</param>
        /// <returns>The decompressed <c>bytes[]</c>.</returns>
        public byte[] Decompress(byte[] compressedBytes, CompressionSettings compressionSettings)
        {
            if (ReferenceEquals(compressedBytes, null))
            {
                return(null);
            }

            if (compressedBytes.Length == 0)
            {
                return(Array.Empty <byte>());
            }

            byte[] decompressedBytes;
            var    decoder = new Decoder();

            MemoryStream input  = new MemoryStream(compressedBytes);
            MemoryStream output = new MemoryStream(compressedBytes.Length / 2 * 3);

            try
            {
                input.Seek(0, 0);

                byte[] props = new byte[5];
                if (input.Read(props, 0, 5) != 5)
                {
                    throw new InvalidDataException($"{nameof(LzmaUtility)}: input LZMA data is too short. Returning null...");
                }

                long outSize = 0;
                for (int i = 0; i < 8; i++)
                {
                    int v = input.ReadByte();
                    if (v < 0)
                    {
                        throw new InvalidDataException($"{nameof(LzmaUtility)}: Can't read byte at {nameof(input)} position \"{input.Position}\"...");
                    }

                    outSize |= ((long)(byte)v) << (8 * i);
                }

                decoder.SetDecoderProperties(props);

                long compressedSize = input.Length - input.Position;
                decoder.Code(input, output, compressedSize, outSize, null);

                decompressedBytes = output.ToArray();
            }
            catch (Exception e)
            {
                string msg = $"{nameof(LzmaUtility)}: Decompression failed; thrown exception: {e.ToString()}";
                throw new InvalidDataException(msg);
            }
            finally
            {
                input.Dispose();
                output.Dispose();
            }

            return(decompressedBytes);
        }
        /// <summary>
        /// Compresses the specified bytes using LZMA.
        /// </summary>
        /// <returns>The compressed <c>byte[]</c> array.</returns>
        /// <param name="bytes">The <c>byte[]</c> array to compress.</param>
        /// <param name="compressionSettings">The desired <see cref="CompressionSettings"/>.</param>
        public byte[] Compress(byte[] bytes, CompressionSettings compressionSettings)
        {
            if (ReferenceEquals(bytes, null))
            {
                return(null);
            }

            if (bytes.Length == 0)
            {
                return(Array.Empty <byte>());
            }

            byte[] compressedBytes;

            var encoder = new Encoder();

            encoder.SetCoderProperties(propIDs, compressionSettings.compressionLevel == CompressionLevel.Optimal ? propertiesOptimal : propertiesNormal);

            MemoryStream input  = new MemoryStream(bytes);
            MemoryStream output = new MemoryStream(bytes.Length / 4 * 3);

            try
            {
                encoder.WriteCoderProperties(output);

                long size = input.Length;
                for (int i = 0; i < 8; i++)
                {
                    output.WriteByte((byte)(size >> (8 * i)));
                }

                encoder.Code(input, output, -1, -1, null);
                compressedBytes = output.ToArray();
            }
            catch
            {
                compressedBytes = null;
            }
            finally
            {
                input.Dispose();
                output.Dispose();
            }

            return(compressedBytes);
        }
        /// <summary>
        /// Decompresses the specified bytes using <see cref="GZipStream"/> and the
        /// <see cref="CompressionSettings"/> that have been used to originally compress the bytes.
        /// </summary>
        /// <param name="gzippedBytes">The gzipped <c>byte[]</c> array that you want to decompress.</param>
        /// <param name="compressionSettings">The <see cref="CompressionSettings"/> that have been used to compress the bytes.</param>
        /// <returns>The decompressed <c>bytes[]</c>.</returns>
        public byte[] Decompress(byte[] gzippedBytes, CompressionSettings compressionSettings)
        {
            if (ReferenceEquals(gzippedBytes, null))
            {
                return(null);
            }

            if (gzippedBytes.Length == 0)
            {
                return(Array.Empty <byte>());
            }

            using MemoryStream input             = new MemoryStream(gzippedBytes);
            using MemoryStream output            = new MemoryStream(gzippedBytes.Length / 2 * 3);
            using GZipStream decompressionStream = new GZipStream(input, CompressionMode.Decompress);

            decompressionStream.CopyTo(output, Math.Max(4096, compressionSettings.bufferSize));
            decompressionStream.Flush();

            return(output.ToArray());
        }
        /// <summary>
        /// Compresses the specified bytes using <see cref="GZipStream"/> and the provided <see cref="CompressionSettings"/>.
        /// </summary>
        /// <returns>The gzipped <c>byte[]</c> array.</returns>
        /// <param name="bytes">The <c>byte[]</c> array to compress.</param>
        /// <param name="compressionSettings">The desired <see cref="CompressionSettings"/>.</param>
        public byte[] Compress(byte[] bytes, CompressionSettings compressionSettings)
        {
            if (ReferenceEquals(bytes, null))
            {
                return(null);
            }

            if (bytes.Length == 0)
            {
                return(Array.Empty <byte>());
            }

            using MemoryStream input           = new MemoryStream(bytes);
            using MemoryStream output          = new MemoryStream(bytes.Length / 4 * 3);
            using GZipStream compressionStream = new GZipStream(output, compressionSettings.compressionLevel);

            input.CopyTo(compressionStream, Math.Max(4096, compressionSettings.bufferSize));
            compressionStream.Flush();

            return(output.ToArray());
        }
        /// <summary>
        /// Decompresses the specified bytes using <see cref="GZipStream"/> and the
        /// <see cref="CompressionSettings"/> that have been used to originally compress the bytes.
        /// </summary>
        /// <param name="gzippedBytes">The gzipped <c>byte[]</c> array that you want to decompress.</param>
        /// <param name="compressionSettings">The <see cref="CompressionSettings"/> that have been used to compress the bytes.</param>
        /// <returns>The decompressed <c>bytes[]</c>.</returns>
        public async Task <byte[]> Decompress(byte[] gzippedBytes, CompressionSettings compressionSettings)
        {
            if (ReferenceEquals(gzippedBytes, null))
            {
                return(null);
            }

            if (gzippedBytes.Length == 0)
            {
                return(Array.Empty <byte>());
            }

            await using MemoryStream input             = new MemoryStream(gzippedBytes);
            await using MemoryStream output            = new MemoryStream(gzippedBytes.Length / 2 * 3);
            await using GZipStream decompressionStream = new GZipStream(input, CompressionMode.Decompress);

            await decompressionStream.CopyToAsync(output, Math.Max(4096, compressionSettings.bufferSize)).ConfigureAwait(false);

            await decompressionStream.FlushAsync().ConfigureAwait(false);

            return(output.ToArray());
        }
        /// <summary>
        /// Compresses the specified bytes using <see cref="GZipStream"/> and the provided <see cref="CompressionSettings"/>.
        /// </summary>
        /// <returns>The gzipped <c>byte[]</c> array.</returns>
        /// <param name="bytes">The <c>byte[]</c> array to compress.</param>
        /// <param name="compressionSettings">The desired <see cref="CompressionSettings"/>.</param>
        public async Task <byte[]> Compress(byte[] bytes, CompressionSettings compressionSettings)
        {
            if (ReferenceEquals(bytes, null))
            {
                return(null);
            }

            if (bytes.Length == 0)
            {
                return(Array.Empty <byte>());
            }

            await using MemoryStream input           = new MemoryStream(bytes);
            await using MemoryStream output          = new MemoryStream(bytes.Length / 4 * 3);
            await using GZipStream compressionStream = new GZipStream(output, compressionSettings.compressionLevel);

            await input.CopyToAsync(compressionStream, Math.Max(4096, compressionSettings.bufferSize)).ConfigureAwait(false);

            await compressionStream.FlushAsync().ConfigureAwait(false);

            return(output.ToArray());
        }
 /// <summary>
 /// Decompresses the specified bytes using LZMA and the
 /// <see cref="CompressionSettings"/> that have been used to originally compress the bytes.
 /// </summary>
 /// <param name="compressedBytes">The compressed <c>byte[]</c> array that you want to decompress.</param>
 /// <param name="compressionSettings">The <see cref="CompressionSettings"/> that have been used to compress the bytes.</param>
 /// <returns>The decompressed <c>bytes[]</c>.</returns>
 public Task <byte[]> Decompress(byte[] compressedBytes, CompressionSettings compressionSettings)
 {
     return(Task.Run(() => lzma.Decompress(compressedBytes, compressionSettings)));
 }
 /// <summary>
 /// Compresses the specified bytes using LZMA.
 /// </summary>
 /// <returns>The compressed <c>byte[]</c> array.</returns>
 /// <param name="bytes">The <c>byte[]</c> array to compress.</param>
 /// <param name="compressionSettings">The desired <see cref="CompressionSettings"/>.</param>
 public Task <byte[]> Compress(byte[] bytes, CompressionSettings compressionSettings)
 {
     return(Task.Run(() => lzma.Compress(bytes, compressionSettings)));
 }