From 9fdabc0f6a153f9526c5ae034caad68e4a40fb9f Mon Sep 17 00:00:00 2001 From: Anairkoen Schno Date: Sun, 14 Nov 2021 22:32:09 -0600 Subject: [PATCH] Add span-based single compression and decompression methods --- .../BrotliSharpLib.Tests.csproj | 2 +- BrotliSharpLib/Brotli.cs | 137 ++++++++++++++++++ BrotliSharpLib/BrotliSharpLib.csproj | 36 +++-- 3 files changed, 165 insertions(+), 10 deletions(-) diff --git a/BrotliSharpLib.Tests/BrotliSharpLib.Tests.csproj b/BrotliSharpLib.Tests/BrotliSharpLib.Tests.csproj index 9ec50b9..6dffbdb 100644 --- a/BrotliSharpLib.Tests/BrotliSharpLib.Tests.csproj +++ b/BrotliSharpLib.Tests/BrotliSharpLib.Tests.csproj @@ -2,7 +2,7 @@ Library - netcoreapp2.2 + net6.0 library diff --git a/BrotliSharpLib/Brotli.cs b/BrotliSharpLib/Brotli.cs index 1b27e89..7437ce2 100644 --- a/BrotliSharpLib/Brotli.cs +++ b/BrotliSharpLib/Brotli.cs @@ -87,6 +87,64 @@ public static unsafe byte[] DecompressBuffer(byte[] buffer, int offset, int leng } } +#if HAS_SPAN + /// + /// Attempts to decompress the compressed data in into . + /// + /// The buffer containing compressed data. + /// The buffer to write the decompressed data to. + /// The number of bytes written to the output buffer. + /// The custom dictionary that will be passed to the decoder. + /// if the input buffer was successfully decompressed, otherwise. + public static unsafe bool TryDecompressBuffer(ReadOnlySpan buffer, Span outBufferData, out int writtenData, ReadOnlySpan customDict = default) + { + // Create the decoder state and intialise it. + var s = BrotliCreateDecoderState(); + BrotliDecoderStateInit(ref s); + + writtenData = 0; + + // Set the custom dictionary + fixed (byte* customDictPtr = customDict) + { + if (customDictPtr != null) + BrotliDecoderSetCustomDictionary(ref s, customDict.Length, customDictPtr); + + // Pin the output buffer and the input buffer. + fixed (byte* outBuffer = outBufferData) + { + fixed (byte* inBuffer = buffer) + { + // Specify the length of the input buffer. + size_t len = buffer.Length; + + // Local vars for input/output buffer. + var bufPtr = inBuffer; + var outPtr = outBuffer; + + // Specify the amount of bytes available in the output buffer. + size_t availOut = outBufferData.Length; + + // Total number of bytes decoded. + size_t total = 0; + + // Main decompression loop. + var result = BrotliDecoderDecompressStream(ref s, &len, &bufPtr, &availOut, &outPtr, &total); + + // Cleanup and throw. + BrotliDecoderStateCleanup(ref s); + + if (result != BrotliDecoderResult.BROTLI_DECODER_RESULT_SUCCESS) + return false; + + writtenData = outBufferData.Length - (int)availOut; + return true; + } + } + } + } +#endif + /// /// Compresses a byte array into a new byte array using brotli. /// @@ -167,5 +225,84 @@ public static unsafe byte[] CompressBuffer(byte[] buffer, int offset, int length return ms.ToArray(); } } + +#if HAS_SPAN + /// + /// Tries to compress into . + /// + /// The buffer to read uncompressed data from. + /// The buffer to write compressed data to. + /// The amount of data written to . + /// The custom dictionary that will be passed to the encoder. + /// if the input buffer was successfully compressed, otherwise. + public static bool TryCompressBuffer(ReadOnlySpan buffer, Span outBuffer, out int writtenData, ReadOnlySpan customDict = default) + => TryCompressBuffer(buffer, outBuffer, out writtenData, customDict); + /// + /// Tries to compress into . + /// + /// The buffer to read uncompressed data from. + /// The buffer to write compressed data to. + /// The amount of data written to . + /// The quality of the compression. This must be a value between 0 to 11 (inclusive). + /// The window size (in bits). This must be a value between 10 and 24 (inclusive). + /// The custom dictionary that will be passed to the encoder. + /// if the input buffer was successfully compressed, otherwise. + public static unsafe bool TryCompressBuffer(ReadOnlySpan buffer, Span outBuffer, out int writtenData, + int quality, int lgwin, ReadOnlySpan customDict = default) + { + // Create the encoder state and intialise it. + var s = BrotliEncoderCreateInstance(null, null, null); + + // Set the encoder parameters + if (quality != -1) + BrotliEncoderSetParameter(ref s, BrotliEncoderParameter.BROTLI_PARAM_QUALITY, (uint)quality); + + if (lgwin != -1) + BrotliEncoderSetParameter(ref s, BrotliEncoderParameter.BROTLI_PARAM_LGWIN, (uint)lgwin); + + // Set the custom dictionary + fixed (byte* cd = customDict) + { + if (cd != null) + BrotliEncoderSetCustomDictionary(ref s, customDict.Length, cd); + } + + writtenData = 0; + + size_t available_in = buffer.Length; + size_t available_out = outBuffer.Length; + fixed (byte* o = outBuffer) + fixed (byte* b = buffer) + { + byte* next_in = b; + byte* next_out = o; + + bool fail = false; + + // Compress stream using inputted buffer + if (!BrotliEncoderCompressStream(ref s, BrotliEncoderOperation.BROTLI_OPERATION_FINISH, + &available_in, &next_in, &available_out, &next_out, null)) + { + fail = true; + } + else + { + // Check that the encoder is finished + if (BrotliEncoderIsFinished(ref s)) + { + fail = true; + } + } + + BrotliEncoderDestroyInstance(ref s); + + if (fail) + return false; + } + + writtenData = outBuffer.Length - (int)available_out; + return true; + } +#endif } } \ No newline at end of file diff --git a/BrotliSharpLib/BrotliSharpLib.csproj b/BrotliSharpLib/BrotliSharpLib.csproj index 94666ff..a43083a 100644 --- a/BrotliSharpLib/BrotliSharpLib.csproj +++ b/BrotliSharpLib/BrotliSharpLib.csproj @@ -6,8 +6,8 @@ 1570;1701 - 0.3.3 - 0.3.3 + 0.3.4 + 0.3.4 master131 Full C# port of Brotli compression library. Copyright (c) 2017-2019 master131 @@ -15,7 +15,7 @@ True git brotli;csharp;net - 0.3.2 + 0.3.4 https://github.com/master131/BrotliSharpLib/blob/master/LICENSE https://github.com/master131/BrotliSharpLib true @@ -29,37 +29,55 @@ $(DefineConstants);SIZE_OF_T + + $(DefineConstants);HAS_SPAN + + + 4.5.4 + - 4.5.2 + 4.5.3 + + 4.5.4 + - 4.5.2 + 4.5.3 + + 4.5.4 + - 4.5.2 + 4.5.3 4.3.0 + + 4.5.4 + - 4.5.2 + 4.5.3 + + 4.5.4 + - 4.5.2 + 4.5.3 - 4.5.2 + 4.5.3 \ No newline at end of file