<|start_filename|>src/EasyCompressor/Compressor/DeflateCompressor.cs<|end_filename|>
﻿using System.IO;
using System.IO.Compression;
using System.Threading;
using System.Threading.Tasks;

namespace EasyCompressor
{
    /// <summary>
    /// Deflate compressor
    /// </summary>
    public class DeflateCompressor : BaseCompressor
    {
        /// <summary>
        /// Compression level
        /// </summary>
        protected readonly CompressionLevel Level;

        /// <summary>
        /// Initializes a new instance
        /// </summary>
        /// <param name="name">Name</param>
        /// <param name="level">Compression level</param>
        public DeflateCompressor(string name = null, CompressionLevel level = CompressionLevel.Optimal)
        {
            Name = name;
            Level = level;
        }

        /// <inheritdoc/>
        protected override byte[] BaseCompress(byte[] bytes)
        {
            using var inputStream = new MemoryStream(bytes);
            using var outputStream = new MemoryStream();
            using (var deflateStream = new DeflateStream(outputStream, Level))
            {
                inputStream.CopyTo(deflateStream, bytes.Length);
                //inputStream.WriteTo(deflateStream);
                //deflateStream.Write(bytes, 0, bytes.Length);

                inputStream.Flush();
                deflateStream.Flush();
            }
            return outputStream.ToArray();
        }

        /// <inheritdoc/>
        protected override byte[] BaseDecompress(byte[] compressedBytes)
        {
            using var inputStream = new MemoryStream(compressedBytes);
            using var outputStream = new MemoryStream();
            using (var deflateStream = new DeflateStream(inputStream, CompressionMode.Decompress))
            {
                deflateStream.CopyTo(outputStream, compressedBytes.Length);

                outputStream.Flush();
                deflateStream.Flush();
            }
            return outputStream.ToArray();
        }

        /// <inheritdoc/>
        protected override void BaseCompress(Stream inputStream, Stream outputStream)
        {
            using var deflateStream = new DeflateStream(outputStream, Level, true);
            inputStream.CopyTo(deflateStream);

            inputStream.Flush();
            deflateStream.Flush();
        }

        /// <inheritdoc/>
        protected override void BaseDecompress(Stream inputStream, Stream outputStream)
        {
            using var deflateStream = new DeflateStream(inputStream, CompressionMode.Decompress, true);
            deflateStream.CopyTo(outputStream);

            outputStream.Flush();
            deflateStream.Flush();
        }

        /// <inheritdoc/>
        protected override async Task BaseCompressAsync(Stream inputStream, Stream outputStream, CancellationToken cancellationToken = default)
        {
            using var deflateStream = new DeflateStream(outputStream, Level, true);
            await inputStream.CopyToAsync(deflateStream, DefaultBufferSize, cancellationToken).ConfigureAwait(false);

            await inputStream.FlushAsync(cancellationToken).ConfigureAwait(false);
            await deflateStream.FlushAsync(cancellationToken).ConfigureAwait(false);
        }

        /// <inheritdoc/>
        protected override async Task BaseDecompressAsync(Stream inputStream, Stream outputStream, CancellationToken cancellationToken = default)
        {
            using var deflateStream = new DeflateStream(inputStream, CompressionMode.Decompress, true);
            await deflateStream.CopyToAsync(outputStream, DefaultBufferSize, cancellationToken).ConfigureAwait(false);

            await outputStream.FlushAsync(cancellationToken).ConfigureAwait(false);
            await deflateStream.FlushAsync(cancellationToken).ConfigureAwait(false);
        }
    }
}


<|start_filename|>test/EasyCompressor.Tests/Data/Serialize.cs<|end_filename|>
﻿using MessagePack;
using Newtonsoft.Json;
using System.IO;

namespace EasyCompressor.Tests
{
    public static class Serializer
    {
        public static byte[] SerializeMessagePack<T>(T value)
        {
            return MessagePackSerializer.Serialize(value);
        }

        public static T DeserializeMessagePack<T>(byte[] bytes)
        {
            return MessagePackSerializer.Deserialize<T>(bytes);
        }

        public static byte[] SerializeMessagePackLz4<T>(T value)
        {
            var lz4Options = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray);
            return MessagePackSerializer.Serialize(value, lz4Options);
        }

        public static T DeserializeMessagePackLz4<T>(byte[] bytes)
        {
            var lz4Options = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray);
            return MessagePackSerializer.Deserialize<T>(bytes, lz4Options);
        }

        public static byte[] SerializeProtobuf<T>(T value)
        {
            using var stream = new MemoryStream();
            ProtoBuf.Serializer.Serialize<T>(stream, value);
            return stream.ToArray();
        }

        public static T DeserializeProtobuf<T>(byte[] bytes)
        {
            using var stream = new MemoryStream(bytes);
            return ProtoBuf.Serializer.Deserialize<T>(stream);
        }

        public static T FromJson<T>(string json)
        {
            return JsonConvert.DeserializeObject<T>(json, Converter.Settings);
        }

        public static class Converter
        {
            public static readonly JsonSerializerSettings Settings = new()
            {
                MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
                DateParseHandling = DateParseHandling.None
            };
        }
    }
}

<|start_filename|>test/EasyCompressor.Benchmark/Benchmarks/BaseBenchmark.cs<|end_filename|>
﻿using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Order;
using EasyCompressor;
using System.Collections.Generic;
using System.IO;

namespace EasySerializer.Benchmark
{
    //[DryJob]
    [ShortRunJob]
    //[SimpleJob(RunStrategy.Throughput)]
    [MemoryDiagnoser]
    [KeepBenchmarkFiles(true)]
    [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByMethod)]
    [Orderer(SummaryOrderPolicy.FastestToSlowest, MethodOrderPolicy.Declared)]
    public class BaseBenchmark
    {
        public BaseBenchmark()
        {
            var json = File.ReadAllText("Data\\spotifyAlbum.json");
            var spotifyAlbums = Serializer.FromJson<SpotifyAlbumArray>(json);
            OriginalBytes = Serializer.SerializeMessagePack(spotifyAlbums);
        }

        protected byte[] OriginalBytes;

        public IEnumerable<object[]> GetArguments
        {
            get
            {
                yield return new object[] { new CompressorArg(new GZipCompressor()), new CompressedArg(new GZipCompressor(), OriginalBytes) };
                yield return new object[] { new CompressorArg(new DeflateCompressor()), new CompressedArg(new DeflateCompressor(), OriginalBytes) };
                yield return new object[] { new CompressorArg(new BrotliCompressor()), new CompressedArg(new BrotliCompressor(), OriginalBytes) };
                yield return new object[] { new CompressorArg(new BrotliNETCompressor()), new CompressedArg(new BrotliNETCompressor(), OriginalBytes) };
                yield return new object[] { new CompressorArg(new LZ4Compressor()), new CompressedArg(new LZ4Compressor(), OriginalBytes) };
                yield return new object[] { new CompressorArg(new LZMACompressor()), new CompressedArg(new LZMACompressor(), OriginalBytes) };
                yield return new object[] { new CompressorArg(new SnappyCompressor()), new CompressedArg(new SnappyCompressor(), OriginalBytes) };
                //yield return new object[] { new CompressorArg(new ZstandardCompressor()), new CompressedArg(new ZstandardCompressor(), OriginalBytes) };
                yield return new object[] { new CompressorArg(new ZstdCompressor()), new CompressedArg(new ZstdCompressor(), OriginalBytes) };
            }
        }

        public class CompressorArg
        {
            public ICompressor Compressor { get; }
            public CompressorArg(ICompressor compressor)
            {
                Compressor = compressor;
            }
            public override string ToString() => Compressor.GetType().Name;
        }

        public class CompressedArg
        {
            private readonly int compressedRatio;
            private readonly decimal savingPercent;
            public byte[] CompressedBytes { get; }

            public CompressedArg(ICompressor compressor, byte[] originalBytes)
            {
                CompressedBytes = compressor.Compress(originalBytes);
                compressedRatio = originalBytes.Length / CompressedBytes.Length;
                savingPercent = (originalBytes.Length - CompressedBytes.Length) * 100 / (decimal)originalBytes.Length;
            }

            public override string ToString() => string.Format("{0} ({1} bytes)", compressedRatio, CompressedBytes.Length);
        }
    }
}


<|start_filename|>src/EasyCaching.Extensions.EasyCompressor/EasyCompressorEasyCachingOptionsExtension.cs<|end_filename|>
﻿using EasyCaching.Core.Configurations;
using EasyCaching.Core.Serialization;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;

namespace EasyCompressor
{
    internal class EasyCompressorEasyCachingOptionsExtension : IEasyCachingOptionsExtension
    {
        private static readonly Dictionary<string, string> _dictonary = new(StringComparer.OrdinalIgnoreCase) { { "binary", null } };

        public EasyCompressorEasyCachingOptionsExtension()
        {
        }

        public EasyCompressorEasyCachingOptionsExtension(string serializerName, string compressorName)
        {
            //Workaround for default BinaryFormatter serializer
            _dictonary["binary"] = compressorName;
            _dictonary[serializerName] = compressorName;
        }

        public void AddServices(IServiceCollection services)
        {
            var descriptors = services.Where(p =>
                p.ServiceType == typeof(IEasyCachingSerializer) &&
                p.ImplementationFactory?.Target.GetType().ReflectedType != typeof(EasyCompressorEasyCachingOptionsExtension)).ToList();

            if (descriptors.Count == 0)
                throw new InvalidOperationException("This operation must be called after serializer added.");

            foreach (var descriptor in descriptors)
            {
                //descriptor.ImplementationType == typeof(DefaultBinaryFormatterSerializer)

                services.Remove(descriptor);

                switch (descriptor.Lifetime)
                {
                    case ServiceLifetime.Singleton:
                        services.AddSingleton(provider => DecoratorFactory(descriptor, provider));
                        break;
                    case ServiceLifetime.Scoped:
                        services.AddScoped(provider => DecoratorFactory(descriptor, provider));
                        break;
                    case ServiceLifetime.Transient:
                        services.AddTransient(provider => DecoratorFactory(descriptor, provider));
                        break;
                }
            }
        }

        private IEasyCachingSerializer DecoratorFactory(ServiceDescriptor descriptor, IServiceProvider provider)
        {
            IEasyCachingSerializer serializer;

            if (descriptor.ImplementationInstance != null)
            {
                serializer = (IEasyCachingSerializer)descriptor.ImplementationInstance;
            }
            else if (descriptor.ImplementationType != null)
            {
                serializer = (IEasyCachingSerializer)ActivatorUtilities.GetServiceOrCreateInstance(provider, descriptor.ImplementationType);
            }
            else //has Factory
            {
                serializer = (IEasyCachingSerializer)descriptor.ImplementationFactory(provider);
            }

            _dictonary.TryGetValue(serializer.Name, out var compressorName);
            var compressor = provider.GetRequiredService<ICompressorProvider>().GetCompressor(compressorName);

            return new EasyCachingSerializerDecorator(compressor, serializer);
        }
    }
}


<|start_filename|>src/EasyCompressor.Zstandard/ZstandardCompressor.cs<|end_filename|>
﻿using System.IO;
using System.IO.Compression;
using System.Threading;
using System.Threading.Tasks;
using Zstandard.Net;

namespace EasyCompressor
{
    /// <summary>
    /// Zstandard compressor
    /// </summary>
    public class ZstandardCompressor : BaseCompressor
    {
        /// <summary>
        /// Level
        /// </summary>
        protected readonly int Level;

        /// <summary>
        /// Initializes a new instance
        /// </summary>
        /// <param name="name">Name</param>
        /// <param name="level">Level</param>
        public ZstandardCompressor(string name = null, int level = 3)
        {
            Name = name;
            Level = level;
        }

        /// <inheritdoc/>
        protected override byte[] BaseCompress(byte[] bytes)
        {
            //using (var outputStream = new MemoryStream())
            //{
            //    using (var zstandardStream = new ZstandardStream(outputStream, DefaultLevel))
            //    {
            //        zstandardStream.Write(bytes, 0, bytes.Length);
            //        zstandardStream.Flush();
            //    }
            //    return outputStream.ToArray();
            //}

            using var inputStream = new MemoryStream(bytes);
            using var outputStream = new MemoryStream();
            using (var zstandardStream = new ZstandardStream(outputStream, Level))
            {
                inputStream.CopyTo(zstandardStream, bytes.Length);
                //inputStream.WriteTo(zstandardStream);
                //zstandardStream.Write(bytes, 0, bytes.Length);

                inputStream.Flush();
                zstandardStream.Flush();
            }
            return outputStream.ToArray();
        }

        /// <inheritdoc/>
        protected override byte[] BaseDecompress(byte[] compressedBytes)
        {
            using var inputStream = new MemoryStream(compressedBytes);
            using var outputStream = new MemoryStream();
            using (var gZipStream = new ZstandardStream(inputStream, CompressionMode.Decompress))
            {
                gZipStream.CopyTo(outputStream, compressedBytes.Length);

                outputStream.Flush();
                gZipStream.Flush();
            }
            return outputStream.ToArray();
        }

        /// <inheritdoc/>
        protected override void BaseCompress(Stream inputStream, Stream outputStream)
        {
            using var gZipStream = new ZstandardStream(outputStream, Level, true);
            inputStream.CopyTo(gZipStream);

            inputStream.Flush();
            gZipStream.Flush();
        }

        /// <inheritdoc/>
        protected override void BaseDecompress(Stream inputStream, Stream outputStream)
        {
            using var gZipStream = new ZstandardStream(inputStream, CompressionMode.Decompress, true);
            gZipStream.CopyTo(outputStream);

            outputStream.Flush();
            gZipStream.Flush();
        }

        /// <inheritdoc/>
        protected override async Task BaseCompressAsync(Stream inputStream, Stream outputStream, CancellationToken cancellationToken = default)
        {
            using var gZipStream = new ZstandardStream(outputStream, Level, true);
            await inputStream.CopyToAsync(gZipStream, DefaultBufferSize, cancellationToken).ConfigureAwait(false);

            await inputStream.FlushAsync(cancellationToken).ConfigureAwait(false);
            await gZipStream.FlushAsync(cancellationToken).ConfigureAwait(false);
        }

        /// <inheritdoc/>
        protected override async Task BaseDecompressAsync(Stream inputStream, Stream outputStream, CancellationToken cancellationToken = default)
        {
            using var gZipStream = new ZstandardStream(inputStream, CompressionMode.Decompress, true);
            await gZipStream.CopyToAsync(outputStream, DefaultBufferSize, cancellationToken).ConfigureAwait(false);

            await outputStream.FlushAsync(cancellationToken).ConfigureAwait(false);
            await gZipStream.FlushAsync(cancellationToken).ConfigureAwait(false);
        }
    }
}


<|start_filename|>test/EasyCompressor.Tests/StreamAsyncTests.cs<|end_filename|>
﻿using NUnit.Framework;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace EasyCompressor.Tests.StreamAsyncTest
{
    public class StreamAsyncTests : TestBase
    {
        public StreamAsyncTests(ICompressor compressor)
            : base(compressor)
        {
        }

        [Test]
        public void Compress_NullValue1_Should_Throws_ArgumentNullException()
        {
            AsyncTestDelegate action = () => Compressor.CompressAsync(null, new MemoryStream());

            Assert.ThrowsAsync<ArgumentNullException>(action);
        }

        [Test]
        public void Compress_NullValue2_Should_Throws_ArgumentNullException()
        {
            AsyncTestDelegate action = () => Compressor.CompressAsync(new MemoryStream(), null);

            Assert.ThrowsAsync<ArgumentNullException>(action);
        }

        [Test]
        public void Decompress_NullValue1_Should_Throws_ArgumentNullException()
        {
            AsyncTestDelegate action = () => Compressor.DecompressAsync(null, new MemoryStream());

            Assert.ThrowsAsync<ArgumentNullException>(action);
        }

        [Test]
        public void Decompress_NullValue2_Should_Throws_ArgumentNullException()
        {
            AsyncTestDelegate action = () => Compressor.DecompressAsync(new MemoryStream(), null);

            Assert.ThrowsAsync<ArgumentNullException>(action);
        }

        [Test]
        public void Compress_Should_DoesNotThrow_Exception()
        {
            using var inputStream = new MemoryStream(ObjectBytes);
            using var outputStream = new MemoryStream();

            AsyncTestDelegate action = () => Compressor.CompressAsync(inputStream, outputStream);

            Assert.DoesNotThrowAsync(action);
        }

        [Test]
        public async Task CompressedResult_ShouldNot_NullOrEmpty()
        {
            using var inputStream = new MemoryStream(ObjectBytes);
            using var compressedStream = new MemoryStream();

            await Compressor.CompressAsync(inputStream, compressedStream).ConfigureAwait(false);

            var compressedBytes = compressedStream.ToArray();

            Assert.IsNotNull(compressedBytes);
            Assert.IsNotEmpty(compressedBytes);
        }

        [Test]
        public async Task Decompress_Should_DoesNotThrow_Exception()
        {
            using var inputStream = new MemoryStream(ObjectBytes);
            using var outputStream = new MemoryStream();

            await Compressor.CompressAsync(inputStream, outputStream).ConfigureAwait(false);
            var compressedBytes = outputStream.ToArray();

            using var inputStream2 = new MemoryStream(compressedBytes);
            using var outputStream2 = new MemoryStream();

            AsyncTestDelegate action = () => Compressor.DecompressAsync(inputStream2, outputStream2);

            Assert.DoesNotThrowAsync(action);
        }

        [Test]
        public async Task DecompressdResult_ShouldNot_NullOrEmpty()
        {
            using var inputStream = new MemoryStream(ObjectBytes);
            using var outputStream = new MemoryStream();

            await Compressor.CompressAsync(inputStream, outputStream).ConfigureAwait(false);
            var compressedBytes = outputStream.ToArray();

            using var inputStream2 = new MemoryStream(compressedBytes);
            using var outputStream2 = new MemoryStream();

            await Compressor.DecompressAsync(inputStream2, outputStream2).ConfigureAwait(false);
            var decompressedBytes = outputStream2.ToArray();

            Assert.IsNotNull(decompressedBytes);
            Assert.IsNotEmpty(decompressedBytes);
        }

        [Test]
        public async Task DecompressedResult_Should_SequenceEqual_With_SourceBytes()
        {
            using var inputStream = new MemoryStream(ObjectBytes);
            using var outputStream = new MemoryStream();

            await Compressor.CompressAsync(inputStream, outputStream).ConfigureAwait(false);
            var compressedBytes = outputStream.ToArray();

            using var inputStream2 = new MemoryStream(compressedBytes);
            using var outputStream2 = new MemoryStream();

            await Compressor.DecompressAsync(inputStream2, outputStream2).ConfigureAwait(false);
            var decompressedBytes = outputStream2.ToArray();

            Assert.True(decompressedBytes.SequenceEqual(ObjectBytes));
        }

        [Test]
        public async Task CompressAndDecompress_ShouldNot_Close_BothStreams()
        {
            using var inputStream = new MemoryStream(ObjectBytes);
            using var outputStream = new MemoryStream();
            await Compressor.CompressAsync(inputStream, outputStream).ConfigureAwait(false);

            Assert.DoesNotThrow(() => inputStream.Position = 0);
            Assert.DoesNotThrow(() => outputStream.Position = 0);

            using var outputStream2 = new MemoryStream();
            await Compressor.DecompressAsync(outputStream, outputStream2).ConfigureAwait(false);

            Assert.DoesNotThrow(() => outputStream.Position = 0);
            Assert.DoesNotThrow(() => outputStream2.Position = 0);
        }
    }
}


<|start_filename|>src/EasyCompressor.Zstd/ZstdCompressor.cs<|end_filename|>
﻿using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using ZstdNet;

namespace EasyCompressor
{
    /// <summary>
    /// Zstd compressor
    /// </summary>
    public class ZstdCompressor : BaseCompressor
    {
        static ZstdCompressor()
        {
            //This is no longer needed according to ZstdNet v1.4.5 update
            #region Load libzstd v1.4.4 DLL
            ////Workaround to fix "System.DllNotFoundException: Unable to load DLL 'libzstd' or one of its dependencies: The specified module could not be found."

            //if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            //    return;

            //var bit = Environment.Is64BitProcess ? "x64" : "x86";
            //var folder = Path.Combine(Path.GetTempPath(), "zstd-v1.4.4", bit);
            //Directory.CreateDirectory(folder);

            //var fileName = Path.Combine(folder, "libzstd.dll");
            //if (!File.Exists(fileName))
            //{
            //    using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream($"libzstd.{bit}.dll"))
            //    using (var file = File.Open(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
            //        stream.CopyTo(file);
            //}

            //var type = typeof(Compressor).Assembly.GetType("ZstdNet.ExternMethods");
            //var method = type.GetMethod("SetDllDirectory", BindingFlags.NonPublic | BindingFlags.Static);
            //method.Invoke(null, new[] { folder });
            #endregion
        }

        /// <summary>
        /// Level
        /// </summary>
        protected readonly int Level;

        /// <summary>
        /// Initializes a new instance
        /// </summary>
        /// <param name="name">Name</param>
        /// <param name="level">Level</param>
        public ZstdCompressor(string name = null, int level = 3)
        {
            Name = name;
            Level = level;
        }

        /// <inheritdoc/>
        protected override byte[] BaseCompress(byte[] bytes)
        {
            byte[] compressedBytes;
            using (var options = new CompressionOptions(Level))
            using (var compressor = new Compressor(options))
            {
                compressedBytes = compressor.Wrap(bytes);
            }
            return compressedBytes;
        }

        /// <inheritdoc/>
        protected override byte[] BaseDecompress(byte[] compressedBytes)
        {
            byte[] decompressedBytes;
            using (var decompressor = new Decompressor())
            {
                decompressedBytes = decompressor.Unwrap(compressedBytes);
            }
            return decompressedBytes;
        }

        /// <inheritdoc/>
        protected override void BaseCompress(Stream inputStream, Stream outputStream)
        {
            using var inputMemory = new MemoryStream();
            inputStream.CopyTo(inputMemory);
            inputStream.Flush();
            inputMemory.Flush();

            var compressedBytes = BaseCompress(inputMemory.ToArray());

            outputStream.Write(compressedBytes, 0, compressedBytes.Length);
            outputStream.Flush();
        }

        /// <inheritdoc/>
        protected override void BaseDecompress(Stream inputStream, Stream outputStream)
        {
            using var inputMemory = new MemoryStream();
            inputStream.CopyTo(inputMemory, DefaultBufferSize);
            inputStream.Flush();
            inputMemory.Flush();

            var compressedBytes = BaseDecompress(inputMemory.ToArray());

            outputStream.Write(compressedBytes, 0, compressedBytes.Length);
            outputStream.Flush();
        }

        /// <inheritdoc/>
        protected override async Task BaseCompressAsync(Stream inputStream, Stream outputStream, CancellationToken cancellationToken = default)
        {
            using var inputMemory = new MemoryStream();
            await inputStream.CopyToAsync(inputMemory, DefaultBufferSize, cancellationToken).ConfigureAwait(false);
            await inputStream.FlushAsync(cancellationToken).ConfigureAwait(false);
            await inputMemory.FlushAsync(cancellationToken).ConfigureAwait(false);

            var compressedBytes = BaseCompress(inputMemory.ToArray());

            await outputStream.WriteAsync(compressedBytes, 0, compressedBytes.Length, cancellationToken).ConfigureAwait(false);
            await outputStream.FlushAsync().ConfigureAwait(false);
        }

        /// <inheritdoc/>
        protected override async Task BaseDecompressAsync(Stream inputStream, Stream outputStream, CancellationToken cancellationToken = default)
        {
            using var inputMemory = new MemoryStream();
            await inputStream.CopyToAsync(inputMemory, DefaultBufferSize, cancellationToken).ConfigureAwait(false);
            await inputStream.FlushAsync(cancellationToken).ConfigureAwait(false);
            await inputMemory.FlushAsync(cancellationToken).ConfigureAwait(false);

            var compressedBytes = BaseDecompress(inputMemory.ToArray());

            await outputStream.WriteAsync(compressedBytes, 0, compressedBytes.Length, cancellationToken).ConfigureAwait(false);
            await outputStream.FlushAsync().ConfigureAwait(false);
        }
    }
}


<|start_filename|>test/EasyCompressor.Benchmark/Benchmarks/BinaryBenchmark.cs<|end_filename|>
﻿using BenchmarkDotNet.Attributes;

namespace EasySerializer.Benchmark
{
    public class BinaryBenchmark : BaseBenchmark
    {
        [Benchmark]
        [ArgumentsSource(nameof(GetArguments))]
        public void Compress(CompressorArg Compressor, CompressedArg CompressionRatio)
        {
            Compressor.Compressor.Compress(OriginalBytes);
        }

        [Benchmark]
        [ArgumentsSource(nameof(GetArguments))]
        public void Decompress(CompressorArg Compressor, CompressedArg CompressionRatio)
        {
            Compressor.Compressor.Decompress(CompressionRatio.CompressedBytes);
        }

        [Benchmark]
        [ArgumentsSource(nameof(GetArguments))]
        public void CompressAndDecompress(CompressorArg Compressor, CompressedArg CompressionRatio)
        {
            var compressedBytes = Compressor.Compressor.Compress(OriginalBytes);
            Compressor.Compressor.Decompress(compressedBytes);
        }
    }
}


<|start_filename|>src/EasyCompressor.Snappy/SnappyCompressor.cs<|end_filename|>
﻿using Snappy;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace EasyCompressor
{
    /// <summary>
    /// Snappy compressor
    /// </summary>
    public class SnappyCompressor : BaseCompressor
    {
        /// <summary>
        /// Initializes a new instance
        /// </summary>
        /// <param name="name">Name</param>
        public SnappyCompressor(string name = null)
        {
            Name = name;
        }

        /// <inheritdoc/>
        protected override byte[] BaseCompress(byte[] bytes)
        {
            return SnappyCodec.Compress(bytes);
        }

        /// <inheritdoc/>
        protected override byte[] BaseDecompress(byte[] compressedBytes)
        {
            return SnappyCodec.Uncompress(compressedBytes);
        }

        /// <inheritdoc/>
        protected override void BaseCompress(Stream inputStream, Stream outputStream)
        {
            using var inputMemory = new MemoryStream();
            inputStream.CopyTo(inputMemory);
            inputStream.Flush();
            inputMemory.Flush();

            var compressedBytes = BaseCompress(inputMemory.ToArray());

            outputStream.Write(compressedBytes, 0, compressedBytes.Length);
            outputStream.Flush();
        }

        /// <inheritdoc/>
        protected override void BaseDecompress(Stream inputStream, Stream outputStream)
        {
            using var inputMemory = new MemoryStream();
            inputStream.CopyTo(inputMemory, DefaultBufferSize);
            inputStream.Flush();
            inputMemory.Flush();

            var compressedBytes = BaseDecompress(inputMemory.ToArray());

            outputStream.Write(compressedBytes, 0, compressedBytes.Length);
            outputStream.Flush();
        }

        /// <inheritdoc/>
        protected override async Task BaseCompressAsync(Stream inputStream, Stream outputStream, CancellationToken cancellationToken = default)
        {
            using var inputMemory = new MemoryStream();
            await inputStream.CopyToAsync(inputMemory, DefaultBufferSize, cancellationToken).ConfigureAwait(false);
            await inputStream.FlushAsync(cancellationToken).ConfigureAwait(false);
            await inputMemory.FlushAsync(cancellationToken).ConfigureAwait(false);

            var compressedBytes = BaseCompress(inputMemory.ToArray());

            await outputStream.WriteAsync(compressedBytes, 0, compressedBytes.Length, cancellationToken).ConfigureAwait(false);
            await outputStream.FlushAsync().ConfigureAwait(false);
        }

        /// <inheritdoc/>
        protected override async Task BaseDecompressAsync(Stream inputStream, Stream outputStream, CancellationToken cancellationToken = default)
        {
            using var inputMemory = new MemoryStream();
            await inputStream.CopyToAsync(inputMemory, DefaultBufferSize, cancellationToken).ConfigureAwait(false);
            await inputStream.FlushAsync(cancellationToken).ConfigureAwait(false);
            await inputMemory.FlushAsync(cancellationToken).ConfigureAwait(false);

            var compressedBytes = BaseDecompress(inputMemory.ToArray());

            await outputStream.WriteAsync(compressedBytes, 0, compressedBytes.Length, cancellationToken).ConfigureAwait(false);
            await outputStream.FlushAsync().ConfigureAwait(false);
        }
    }
}


<|start_filename|>test/EasyCompressor.Tests/TestBase.cs<|end_filename|>
﻿using NUnit.Framework;
using System.Collections;
using System.IO;

namespace EasyCompressor.Tests
{
    [TestFixtureSource(nameof(GetCompressors))]
    public class TestBase
    {
        protected readonly ICompressor Compressor;
        protected byte[] ObjectBytes;

        public TestBase(ICompressor compressor)
        {
            Compressor = compressor;
        }

        [OneTimeSetUp]
        public void OneTimeSetUp()
        {
            var json = File.ReadAllText("Data\\spotifyAlbum.json");
            var spotify = Serializer.FromJson<SpotifyAlbum>(json);
            ObjectBytes = Serializer.SerializeMessagePack(spotify);
        }

        public static IEnumerable GetCompressors
        {
            get
            {
                yield return new TestFixtureData(new GZipCompressor()).SetArgDisplayNames(" GZip ");
                yield return new TestFixtureData(new DeflateCompressor()).SetArgDisplayNames(" Deflate ");
                yield return new TestFixtureData(new BrotliCompressor()).SetArgDisplayNames(" Brotli ");
                yield return new TestFixtureData(new BrotliNETCompressor()).SetArgDisplayNames(" BrotliNET ");
                yield return new TestFixtureData(new LZ4Compressor()).SetArgDisplayNames(" LZ4 ");
                yield return new TestFixtureData(new LZMACompressor()).SetArgDisplayNames(" LZMA ");
                yield return new TestFixtureData(new SnappyCompressor()).SetArgDisplayNames(" Snappy ");
                yield return new TestFixtureData(new ZstandardCompressor()).SetArgDisplayNames(" Zstandard ");
                yield return new TestFixtureData(new ZstdCompressor()).SetArgDisplayNames(" Zstd ");
            }
        }
    }
}


<|start_filename|>src/EasyCompressor.BrotliNET/BrotliNETCompressor.cs<|end_filename|>
﻿using System.IO;
using System.IO.Compression;
using System.Threading;
using System.Threading.Tasks;

namespace EasyCompressor
{
    /// <summary>
    /// BrotliNET compressor
    /// </summary>
    public class BrotliNETCompressor : BaseCompressor
    {
        /// <summary>
        /// Quality
        /// </summary>
        protected readonly uint Quality;

        /// <summary>
        /// Window
        /// </summary>
        protected readonly uint Window;

        /// <summary>
        /// Initializes a new instance
        /// </summary>
        /// <param name="name">Name</param>
        /// <param name="quality">Quality</param>
        /// <param name="window">Window</param>
        public BrotliNETCompressor(string name = null, uint quality = 11, uint window = 22)
        {
            Name = name;
            Quality = quality;
            Window = window;
        }

        /// <inheritdoc/>
        protected override byte[] BaseCompress(byte[] bytes)
        {
            using var inputStream = new MemoryStream(bytes);
            using var outputStream = new MemoryStream();
            using (var brotliStream = new Brotli.BrotliStream(outputStream, CompressionMode.Compress))
            {
                brotliStream.SetQuality(Quality);
                brotliStream.SetWindow(Window);
                inputStream.CopyTo(brotliStream, bytes.Length);
                //inputStream.WriteTo(brotliStream);
                //brotliStream.Write(bytes, 0, bytes.Length);

                inputStream.Flush();
                brotliStream.Flush();
            }
            return outputStream.ToArray();
        }

        /// <inheritdoc/>
        protected override byte[] BaseDecompress(byte[] compressedBytes)
        {
            using var inputStream = new MemoryStream(compressedBytes);
            using var outputStream = new MemoryStream();
            using (var brotliStream = new Brotli.BrotliStream(inputStream, CompressionMode.Decompress))
            {
                brotliStream.CopyTo(outputStream, compressedBytes.Length);

                outputStream.Flush();
                brotliStream.Flush();
            }
            return outputStream.ToArray();
        }

        /// <inheritdoc/>
        protected override void BaseCompress(Stream inputStream, Stream outputStream)
        {
            using var brotliStream = new Brotli.BrotliStream(outputStream, CompressionMode.Compress, true);
            brotliStream.SetQuality(Quality);
            brotliStream.SetWindow(Window);
            inputStream.CopyTo(brotliStream);

            inputStream.Flush();
            brotliStream.Flush();
        }

        /// <inheritdoc/>
        protected override void BaseDecompress(Stream inputStream, Stream outputStream)
        {
            using var brotliStream = new Brotli.BrotliStream(inputStream, CompressionMode.Decompress, true);
            brotliStream.CopyTo(outputStream);

            outputStream.Flush();
            brotliStream.Flush();
        }

        /// <inheritdoc/>
        protected override async Task BaseCompressAsync(Stream inputStream, Stream outputStream, CancellationToken cancellationToken = default)
        {
            using var brotliStream = new Brotli.BrotliStream(outputStream, CompressionMode.Compress, true);
            brotliStream.SetQuality(Quality);
            brotliStream.SetWindow(Window);
            await inputStream.CopyToAsync(brotliStream, DefaultBufferSize, cancellationToken).ConfigureAwait(false);

            await inputStream.FlushAsync(cancellationToken).ConfigureAwait(false);
            await brotliStream.FlushAsync(cancellationToken).ConfigureAwait(false);
        }

        /// <inheritdoc/>
        protected override async Task BaseDecompressAsync(Stream inputStream, Stream outputStream, CancellationToken cancellationToken = default)
        {
            using var brotliStream = new Brotli.BrotliStream(inputStream, CompressionMode.Decompress, true);
            await brotliStream.CopyToAsync(outputStream, DefaultBufferSize, cancellationToken).ConfigureAwait(false);

            await outputStream.FlushAsync(cancellationToken).ConfigureAwait(false);
            await brotliStream.FlushAsync(cancellationToken).ConfigureAwait(false);
        }
    }
}


<|start_filename|>test/EasyCompressor.Benchmark/Program.cs<|end_filename|>
﻿using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;
using System.IO;

namespace EasySerializer.Benchmark
{
#pragma warning disable RCS1018 // Add accessibility modifiers (or vice versa).
#pragma warning disable S1118 // Utility classes should not have public constructors
    class Program
#pragma warning restore S1118 // Utility classes should not have public constructors
#pragma warning restore RCS1018 // Add accessibility modifiers (or vice versa).
    {
        static void Main(string[] args)
        {
#if DEBUG
            System.Console.ForegroundColor = System.ConsoleColor.Yellow;
            System.Console.WriteLine("*****To achieve accurate results, set project configuration to Release mode.*****");
            return;
#endif
#pragma warning disable CS0162 // Unreachable code detected
            BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run();
#pragma warning restore CS0162 // Unreachable code detected
        }
    }
}


<|start_filename|>test/EasyCompressor.Benchmark/Benchmarks/StreamAsyncBenchmark.cs<|end_filename|>
﻿using BenchmarkDotNet.Attributes;
using System.IO;
using System.Threading.Tasks;

namespace EasySerializer.Benchmark
{
    public class StreamAsyncBenchmark : BaseBenchmark
    {
        [Benchmark]
        [ArgumentsSource(nameof(GetArguments))]
        public Task Compress(CompressorArg Compressor, CompressedArg CompressionRatio)
        {
            using var inputStream = new MemoryStream(OriginalBytes);
            using var outputStream = new MemoryStream();

            return Compressor.Compressor.CompressAsync(inputStream, outputStream);
        }

        [Benchmark]
        [ArgumentsSource(nameof(GetArguments))]
        public Task Decompress(CompressorArg Compressor, CompressedArg CompressionRatio)
        {
            using var inputStream = new MemoryStream(CompressionRatio.CompressedBytes);
            using var outputStream = new MemoryStream();

            return Compressor.Compressor.DecompressAsync(inputStream, outputStream);
        }

        [Benchmark]
        [ArgumentsSource(nameof(GetArguments))]
        public async Task CompressAndDecompress(CompressorArg Compressor, CompressedArg CompressionRatio)
        {
            using var inputStream = new MemoryStream(OriginalBytes);
            using var outputStream = new MemoryStream();

            await Compressor.Compressor.CompressAsync(inputStream, outputStream).ConfigureAwait(false);
            var compressedBytes = outputStream.ToArray();

            using var inputStream2 = new MemoryStream(compressedBytes);
            using var outputStream2 = new MemoryStream();

            await Compressor.Compressor.DecompressAsync(inputStream2, outputStream2).ConfigureAwait(false);
        }
    }
}


<|start_filename|>src/EasyCompressor.LZ4/LZ4Compressor.cs<|end_filename|>
﻿using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using K4os.Compression.LZ4;
using K4os.Compression.LZ4.Streams;

namespace EasyCompressor
{
    /// <summary>
    /// LZ4 compressor
    /// </summary>
    public class LZ4Compressor : BaseCompressor
    {
        /// <summary>
        /// LZ4Level
        /// </summary>
        protected readonly LZ4Level Level;

        /// <summary>
        /// Initializes a new instance
        /// </summary>
        /// <param name="name">Name</param>
        /// <param name="level">LZ4Level</param>
        public LZ4Compressor(string name = null, LZ4Level level = LZ4Level.L00_FAST)
        {
            Name = name;
            Level = level;
        }

        /// <inheritdoc/>
        protected override byte[] BaseCompress(byte[] bytes)
        {
            var source = bytes.AsSpan();
            var target = new byte[LZ4Codec.MaximumOutputSize(source.Length) + 4].AsSpan();
            var size = BitConverter.GetBytes(source.Length).AsSpan();
            size.CopyTo(target);
            int compressedBytesSize = LZ4Codec.Encode(source, target.Slice(4), Level);
            return target.Slice(0, compressedBytesSize + 4).ToArray();
        }

        /// <inheritdoc/>
        protected override byte[] BaseDecompress(byte[] compressedBytes)
        {
            var source = compressedBytes.AsSpan();
            var size = source.Slice(0, 4).ToArray();
            var length = BitConverter.ToInt32(size, 0);
            var target = new byte[length].AsSpan();
            int decoded = LZ4Codec.Decode(source.Slice(4), target);
            return target.Slice(0, decoded).ToArray();
        }

        /// <inheritdoc/>
        protected override void BaseCompress(Stream inputStream, Stream outputStream)
        {
            using var lz4Stream = LZ4Stream.Encode(outputStream, Level, leaveOpen: true);
            inputStream.CopyTo(lz4Stream);

            inputStream.Flush();
            lz4Stream.Flush();
        }

        /// <inheritdoc/>
        protected override void BaseDecompress(Stream inputStream, Stream outputStream)
        {
            using var lz4Stream = LZ4Stream.Decode(inputStream, leaveOpen: true);
            lz4Stream.CopyTo(outputStream);

            inputStream.Flush();
            lz4Stream.Flush();
        }

        /// <inheritdoc/>
        protected override async Task BaseCompressAsync(Stream inputStream, Stream outputStream, CancellationToken cancellationToken = default)
        {
            using var lz4Stream = LZ4Stream.Encode(outputStream, Level, leaveOpen: true);
            await inputStream.CopyToAsync(lz4Stream, DefaultBufferSize, cancellationToken).ConfigureAwait(false);

            await inputStream.FlushAsync(cancellationToken).ConfigureAwait(false);
            await lz4Stream.FlushAsync(cancellationToken).ConfigureAwait(false);
        }

        /// <inheritdoc/>
        protected override async Task BaseDecompressAsync(Stream inputStream, Stream outputStream, CancellationToken cancellationToken = default)
        {
            using var lz4Stream = LZ4Stream.Decode(inputStream, leaveOpen: true);
            await lz4Stream.CopyToAsync(outputStream, DefaultBufferSize, cancellationToken).ConfigureAwait(false);

            await inputStream.FlushAsync(cancellationToken).ConfigureAwait(false);
            await lz4Stream.FlushAsync(cancellationToken).ConfigureAwait(false);
        }
    }
}
