aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-08-14 11:43:35 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2023-08-14 11:43:35 -0400
commit3f5eb61fc7166674a5424d5f8e8c23a775c27614 (patch)
tree55a759d2cb0906251ac2dd5aec5e8551b7fb7594 /lib
parent692398494aa9dc49738da3a12e0b884ee4e56287 (diff)
Compression api exposed and tests updated
Diffstat (limited to 'lib')
-rw-r--r--lib/Net.Compression/VNLib.Net.Compression/CompressionExtensions.cs75
-rw-r--r--lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs59
-rw-r--r--lib/Net.Compression/VNLib.Net.Compression/INativeCompressionLib.cs66
-rw-r--r--lib/Net.Compression/VNLib.Net.Compression/INativeCompressor.cs79
-rw-r--r--lib/Net.Compression/VNLib.Net.Compression/LibraryWrapper.cs33
-rw-r--r--lib/Net.Compression/VNLib.Net.Compression/NativeCompressionLib.cs141
-rw-r--r--lib/Net.Compression/VNLib.Net.Compression/SafeCompressorHandle.cs45
-rw-r--r--lib/Net.Compression/VNLib.Net.CompressionTests/CompressorManagerTests.cs208
-rw-r--r--lib/Net.Compression/vnlib_compress/compression.c6
-rw-r--r--lib/Net.Http/src/Core/Response/ResponseWriter.cs3
-rw-r--r--lib/WinRpMalloc/Taskfile.yaml2
11 files changed, 596 insertions, 121 deletions
diff --git a/lib/Net.Compression/VNLib.Net.Compression/CompressionExtensions.cs b/lib/Net.Compression/VNLib.Net.Compression/CompressionExtensions.cs
new file mode 100644
index 0000000..0a4f7aa
--- /dev/null
+++ b/lib/Net.Compression/VNLib.Net.Compression/CompressionExtensions.cs
@@ -0,0 +1,75 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Compression
+* File: CompressionExtensions.cs
+*
+* CompressionExtensions.cs is part of VNLib.Net.Compression which is part of
+* the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Compression is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Net.Compression is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Net.Compression. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System;
+using System.Buffers;
+
+using VNLib.Net.Http;
+
+namespace VNLib.Net.Compression
+{
+ internal static class CompressionExtensions
+ {
+ /// <summary>
+ /// Compresses a block using the compressor context pointer provided
+ /// </summary>
+ /// <param name="nativeLib"></param>
+ /// <param name="comp">A pointer to the compressor context</param>
+ /// <param name="output">A buffer to write the result to</param>
+ /// <param name="input">The input block of memory to compress</param>
+ /// <param name="finalBlock">A value that indicates if a flush is requested</param>
+ /// <returns>The results of the compression operation</returns>
+ public static unsafe CompressionResult CompressBlock(this LibraryWrapper nativeLib, IntPtr comp, Memory<byte> output, ReadOnlyMemory<byte> input, bool finalBlock)
+ {
+ //get pointers to the input and output buffers
+ using MemoryHandle inPtr = input.Pin();
+ using MemoryHandle outPtr = output.Pin();
+
+ //Create the operation struct
+ CompressionOperation operation;
+ CompressionOperation* op = &operation;
+
+ op->flush = finalBlock ? 1 : 0;
+ op->bytesRead = 0;
+ op->bytesWritten = 0;
+
+ //Configure the input and output buffers
+ op->inputBuffer = inPtr.Pointer;
+ op->inputSize = input.Length;
+
+ op->outputBuffer = outPtr.Pointer;
+ op->outputSize = output.Length;
+
+ //Call the native compress function
+ nativeLib!.CompressBlock(comp, &operation);
+
+ //Return the number of bytes written
+ return new()
+ {
+ BytesRead = op->bytesRead,
+ BytesWritten = op->bytesWritten
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs b/lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs
index 5a86c62..bcc85c9 100644
--- a/lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs
+++ b/lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs
@@ -35,10 +35,10 @@
*/
using System;
-using System.Buffers;
using System.Text.Json;
using System.Diagnostics;
using System.IO.Compression;
+using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using VNLib.Net.Http;
@@ -46,6 +46,10 @@ using VNLib.Utils.Logging;
namespace VNLib.Net.Compression
{
+
+ /// <summary>
+ /// A compressor manager that implements the IHttpCompressorManager interface, for runtime loading.
+ /// </summary>
public sealed class CompressorManager : IHttpCompressorManager
{
const string NATIVE_LIB_NAME = "vnlib_compress.dll";
@@ -85,7 +89,7 @@ namespace VNLib.Net.Compression
log?.Debug("Attempting to load native compression library from: {lib}", libPath);
//Load the native library
- _nativeLib = LibraryWrapper.LoadLibrary(libPath);
+ _nativeLib = LibraryWrapper.LoadLibrary(libPath, DllImportSearchPath.SafeDirectories);
log?.Debug("Loaded native compression library with compression level {l}", _compLevel.ToString());
}
@@ -102,26 +106,17 @@ namespace VNLib.Net.Compression
}
///<inheritdoc/>
- public object AllocCompressor()
- {
- return new Compressor();
- }
+ public object AllocCompressor() => new Compressor();
///<inheritdoc/>
public int InitCompressor(object compressorState, CompressionMethod compMethod)
{
- //For now do not allow empty compression methods, later we should allow this to be used as a passthrough
- if(compMethod == CompressionMethod.None)
- {
- throw new ArgumentException("Compression method cannot be None", nameof(compMethod));
- }
-
Compressor compressor = Unsafe.As<Compressor>(compressorState) ?? throw new ArgumentNullException(nameof(compressorState));
//Instance should be null during initialization calls
Debug.Assert(compressor.Instance == IntPtr.Zero, "Init was called but and old compressor instance was not properly freed");
- //Alloc the compressor
+ //Alloc the compressor, let native lib raise exception for supported methods
compressor.Instance = _nativeLib!.AllocateCompressor(compMethod, _compLevel);
//Return the compressor block size
@@ -156,7 +151,7 @@ namespace VNLib.Net.Compression
}
//Force a flush until no more data is available
- CompressionResult result = CompressBlock(compressor.Instance, output, default, true);
+ CompressionResult result = _nativeLib.CompressBlock(compressor.Instance, output, default, true);
return result.BytesWritten;
}
@@ -171,41 +166,9 @@ namespace VNLib.Net.Compression
}
//Compress the block
- return CompressBlock(compressor.Instance, output, input, false);
+ return _nativeLib.CompressBlock(compressor.Instance, output, input, false);
}
-
- private unsafe CompressionResult CompressBlock(IntPtr comp, Memory<byte> output, ReadOnlyMemory<byte> input, bool finalBlock)
- {
- //get pointers to the input and output buffers
- using MemoryHandle inPtr = input.Pin();
- using MemoryHandle outPtr = output.Pin();
-
- //Create the operation struct
- CompressionOperation operation;
- CompressionOperation* op = &operation;
-
- op->flush = finalBlock ? 1 : 0;
- op->bytesRead = 0;
- op->bytesWritten = 0;
-
- //Configure the input and output buffers
- op->inputBuffer = inPtr.Pointer;
- op->inputSize = input.Length;
-
- op->outputBuffer = outPtr.Pointer;
- op->outputSize = output.Length;
-
- //Call the native compress function
- _nativeLib!.CompressBlock(comp, &operation);
-
- //Return the number of bytes written
- return new()
- {
- BytesRead = op->bytesRead,
- BytesWritten = op->bytesWritten
- };
- }
-
+
/*
* A class to contain the compressor state
diff --git a/lib/Net.Compression/VNLib.Net.Compression/INativeCompressionLib.cs b/lib/Net.Compression/VNLib.Net.Compression/INativeCompressionLib.cs
new file mode 100644
index 0000000..878dc5c
--- /dev/null
+++ b/lib/Net.Compression/VNLib.Net.Compression/INativeCompressionLib.cs
@@ -0,0 +1,66 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Compression
+* File: INativeCompressionLib.cs
+*
+* INativeCompressionLib.cs is part of VNLib.Net.Compression which is part of
+* the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Compression is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Net.Compression is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Net.Compression. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System;
+using System.IO.Compression;
+using System.Runtime.InteropServices;
+
+using VNLib.Net.Http;
+
+namespace VNLib.Net.Compression
+{
+ /// <summary>
+ /// Represents a native compression library that can create native
+ /// compressor instances.
+ /// </summary>
+ public interface INativeCompressionLib
+ {
+ /// <summary>
+ /// Gets the compression methods supported by the underluing library
+ /// </summary>
+ /// <returns>The supported compression methods</returns>
+ CompressionMethod GetSupportedMethods();
+
+ /// <summary>
+ /// Allocates a new <see cref="INativeCompressor"/> implementation that allows for
+ /// compressing stream data.
+ /// </summary>
+ /// <param name="method">The desired <see cref="CompressionMethod"/>, must be a supported method</param>
+ /// <param name="level">The desired <see cref="CompressionLevel"/> to compress blocks with</param>
+ /// <returns>The new <see cref="INativeCompressor"/></returns>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="NotSupportedException">The the level or method are not supported by the underlying library</exception>
+ INativeCompressor AllocCompressor(CompressionMethod method, CompressionLevel level);
+
+ /// <summary>
+ /// Allocates a safe compressor handle to allow native operations if preferred.
+ /// </summary>
+ ///<param name="method">The desired <see cref="CompressionMethod"/>, must be a supported method</param>
+ /// <param name="level">The desired <see cref="CompressionLevel"/> to compress blocks with</param>
+ /// <returns>A new <see cref="SafeHandle"/> that holds a pointer to the native compressor context</returns>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="NotSupportedException">The the level or method are not supported by the underlying library</exception>
+ SafeHandle AllocSafeCompressorHandle(CompressionMethod method, CompressionLevel level);
+ }
+} \ No newline at end of file
diff --git a/lib/Net.Compression/VNLib.Net.Compression/INativeCompressor.cs b/lib/Net.Compression/VNLib.Net.Compression/INativeCompressor.cs
new file mode 100644
index 0000000..3455ea0
--- /dev/null
+++ b/lib/Net.Compression/VNLib.Net.Compression/INativeCompressor.cs
@@ -0,0 +1,79 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Compression
+* File: INativeCompressor.cs
+*
+* INativeCompressor.cs is part of VNLib.Net.Compression which is part of
+* the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Compression is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Net.Compression is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Net.Compression. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System;
+using System.IO.Compression;
+
+using VNLib.Net.Http;
+
+namespace VNLib.Net.Compression
+{
+ /// <summary>
+ /// Represents a native compressor instance
+ /// </summary>
+ public interface INativeCompressor : IDisposable
+ {
+ /// <summary>
+ /// Gets the underlying compressor type
+ /// </summary>
+ /// <returns>The underlying compressor type</returns>
+ CompressionMethod GetCompressionMethod();
+
+ /// <summary>
+ /// Gets the underlying compressor's compression level
+ /// </summary>
+ /// <returns>The configured <see cref="CompressionLevel"/> of the current compressor</returns>
+ CompressionLevel GetCompressionLevel();
+
+ /// <summary>
+ /// Flushes all remaining data in the compressor to the output buffer
+ /// </summary>
+ /// <param name="buffer">The output buffer to write flushed compressor data to</param>
+ /// <returns>The number of bytes written to the output buffer</returns>
+ int Flush(Memory<byte> buffer);
+
+ /// <summary>
+ /// Compresses the input block and writes the compressed data to the output block
+ /// </summary>
+ /// <param name="input">The input buffer to compress</param>
+ /// <param name="bytesRead">The number of bytes read by the compressor</param>
+ /// <param name="output">The output buffer to write compressed data to</param>
+ /// <param name="bytesWritten">The number of bytes written to the output buffer</param>
+ CompressionResult Compress(ReadOnlyMemory<byte> input, Memory<byte> output);
+
+ /// <summary>
+ /// Gets the compressor block size if configured
+ /// </summary>
+ /// <returns>The ideal input buffer size for compressing blocks, or <![CDATA[<1]]> if block size is unlimited</returns>
+ int GetBlockSize();
+
+ /// <summary>
+ /// Determines the maximum number of output bytes for the given number of input bytes
+ /// specified by the size parameter
+ /// </summary>
+ /// <param name="size">The number of bytes to get the compressed size of</param>
+ /// <returns>The maxium size of the compressed data</returns>
+ int GetCompressedSize(int size);
+ }
+} \ No newline at end of file
diff --git a/lib/Net.Compression/VNLib.Net.Compression/LibraryWrapper.cs b/lib/Net.Compression/VNLib.Net.Compression/LibraryWrapper.cs
index 0f86107..2ee6018 100644
--- a/lib/Net.Compression/VNLib.Net.Compression/LibraryWrapper.cs
+++ b/lib/Net.Compression/VNLib.Net.Compression/LibraryWrapper.cs
@@ -85,10 +85,10 @@ namespace VNLib.Net.Compression
/// and used for the lifetime of the application.
/// </para>
/// </summary>
- internal sealed class LibraryWrapper
+ internal sealed class LibraryWrapper
{
private readonly SafeLibraryHandle _lib;
- private readonly MethodTable _methodTable;
+ private MethodTable _methodTable;
public string LibFilePath { get; }
@@ -104,10 +104,10 @@ namespace VNLib.Net.Compression
/// </summary>
/// <param name="filePath">The path to the native library to load</param>
/// <returns>The native library wrapper</returns>
- public static LibraryWrapper LoadLibrary(string filePath)
+ public static LibraryWrapper LoadLibrary(string filePath, DllImportSearchPath searchType)
{
//Load the library into the current process
- SafeLibraryHandle lib = SafeLibraryHandle.LoadLibrary(filePath, DllImportSearchPath.SafeDirectories);
+ SafeLibraryHandle lib = SafeLibraryHandle.LoadLibrary(filePath, searchType);
try
{
@@ -223,6 +223,14 @@ namespace VNLib.Net.Compression
}
/// <summary>
+ /// Frees the specified compressor instance without raising exceptions
+ /// </summary>
+ /// <param name="compressor">A pointer to the valid compressor instance to free</param>
+ /// <returns>A value indicating the result of the free operation</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int FreeSafeCompressor(IntPtr compressor) => _methodTable.Free(compressor);
+
+ /// <summary>
/// Determines the output size of a given input size and flush mode for the specified compressor
/// </summary>
/// <param name="compressor">A pointer to the compressor instance</param>
@@ -255,6 +263,23 @@ namespace VNLib.Net.Compression
return result;
}
+ ///<inheritdoc/>
+ ~LibraryWrapper()
+ {
+ _methodTable = default;
+ _lib.Dispose();
+ }
+
+ /// <summary>
+ /// Manually releases the library
+ /// </summary>
+ internal void ManualRelease()
+ {
+ _methodTable = default;
+ _lib.Dispose();
+ GC.SuppressFinalize(this);
+ }
+
private readonly struct MethodTable
{
public GetSupportedMethodsDelegate GetMethods { get; init; }
diff --git a/lib/Net.Compression/VNLib.Net.Compression/NativeCompressionLib.cs b/lib/Net.Compression/VNLib.Net.Compression/NativeCompressionLib.cs
new file mode 100644
index 0000000..f5b9071
--- /dev/null
+++ b/lib/Net.Compression/VNLib.Net.Compression/NativeCompressionLib.cs
@@ -0,0 +1,141 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Compression
+* File: NativeCompressionLib.cs
+*
+* NativeCompressionLib.cs is part of VNLib.Net.Compression which is part of
+* the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Compression is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Net.Compression is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Net.Compression. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System;
+using System.IO.Compression;
+using System.Runtime.InteropServices;
+
+using VNLib.Net.Http;
+using VNLib.Utils;
+using VNLib.Utils.Extensions;
+
+namespace VNLib.Net.Compression
+{
+ /// <summary>
+ /// A referrence native compression library implementation. Allows for creating compressor instances
+ /// from a native dll.
+ /// </summary>
+ public sealed class NativeCompressionLib : VnDisposeable, INativeCompressionLib
+ {
+ private readonly LibraryWrapper _library;
+
+ private NativeCompressionLib(LibraryWrapper nativeLib) => _library = nativeLib;
+
+ ///<inheritdoc/>
+ protected override void Free() => _library.ManualRelease();
+
+ /// <summary>
+ /// Loads the native compression DLL at the specified file path and search pattern
+ /// </summary>
+ /// <param name="libPath">The path (relative or absolute) path to the native dll to load</param>
+ /// <param name="searchPath">The dll search pattern</param>
+ /// <returns>A new <see cref="NativeCompressionLib"/> library handle</returns>
+ public static NativeCompressionLib LoadLibrary(string libPath, DllImportSearchPath searchPath)
+ {
+ LibraryWrapper wrapper = LibraryWrapper.LoadLibrary(libPath, searchPath);
+ return new NativeCompressionLib(wrapper);
+ }
+
+ ///<inheritdoc/>
+ ///<exception cref="NativeCompressionException"></exception>
+ public CompressionMethod GetSupportedMethods() => _library.GetSupportedMethods();
+
+ ///<inheritdoc/>
+ ///<exception cref="NativeCompressionException"></exception>
+ public INativeCompressor AllocCompressor(CompressionMethod method, CompressionLevel level)
+ {
+#pragma warning disable CA2000 // Dispose objects before losing scope
+
+ SafeHandle libHandle = AllocSafeCompressorHandle(method, level);
+ return new Compressor(_library, libHandle);
+
+#pragma warning restore CA2000 // Dispose objects before losing scope
+ }
+
+ ///<inheritdoc/>
+ ///<exception cref="NativeCompressionException"></exception>
+ public SafeHandle AllocSafeCompressorHandle(CompressionMethod method, CompressionLevel level)
+ {
+ //Alloc compressor then craete a safe handle
+ IntPtr comp = _library.AllocateCompressor(method, level);
+ return new SafeCompressorHandle(_library, comp);
+ }
+
+ internal sealed record class Compressor(LibraryWrapper LibComp, SafeHandle Handle) : INativeCompressor
+ {
+
+ ///<inheritdoc/>
+ public CompressionResult Compress(ReadOnlyMemory<byte> input, Memory<byte> output)
+ {
+ Handle.ThrowIfClosed();
+ IntPtr compressor = Handle.DangerousGetHandle();
+ return LibComp.CompressBlock(compressor, output, input, false);
+ }
+
+ ///<inheritdoc/>
+ public void Dispose() => Handle.Dispose();
+
+ ///<inheritdoc/>
+ public int Flush(Memory<byte> buffer)
+ {
+ Handle.ThrowIfClosed();
+ IntPtr compressor = Handle.DangerousGetHandle();
+ CompressionResult result = LibComp.CompressBlock(compressor, buffer, ReadOnlyMemory<byte>.Empty, true);
+ return result.BytesWritten;
+ }
+
+ ///<inheritdoc/>
+ public int GetBlockSize()
+ {
+ Handle.ThrowIfClosed();
+ IntPtr compressor = Handle.DangerousGetHandle();
+ return LibComp.GetBlockSize(compressor);
+ }
+
+ ///<inheritdoc/>
+ public int GetCompressedSize(int size)
+ {
+ Handle.ThrowIfClosed();
+ IntPtr compressor = Handle.DangerousGetHandle();
+ return LibComp.GetOutputSize(compressor, size, 1);
+ }
+
+ ///<inheritdoc/>
+ public CompressionLevel GetCompressionLevel()
+ {
+ Handle.ThrowIfClosed();
+ IntPtr compressor = Handle.DangerousGetHandle();
+ return LibComp.GetCompressorLevel(compressor);
+ }
+
+ ///<inheritdoc/>
+ public CompressionMethod GetCompressionMethod()
+ {
+ Handle.ThrowIfClosed();
+ IntPtr compressor = Handle.DangerousGetHandle();
+ return LibComp.GetCompressorType(compressor);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/lib/Net.Compression/VNLib.Net.Compression/SafeCompressorHandle.cs b/lib/Net.Compression/VNLib.Net.Compression/SafeCompressorHandle.cs
new file mode 100644
index 0000000..4c21b6b
--- /dev/null
+++ b/lib/Net.Compression/VNLib.Net.Compression/SafeCompressorHandle.cs
@@ -0,0 +1,45 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Compression
+* File: SafeCompressorHandle.cs
+*
+* SafeCompressorHandle.cs is part of VNLib.Net.Compression which is part of
+* the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Compression is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Net.Compression is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Net.Compression. If not, see http://www.gnu.org/licenses/.
+*/
+
+
+using System;
+
+using Microsoft.Win32.SafeHandles;
+
+namespace VNLib.Net.Compression
+{
+ internal sealed class SafeCompressorHandle : SafeHandleZeroOrMinusOneIsInvalid
+ {
+ private readonly LibraryWrapper _library;
+
+ internal SafeCompressorHandle(LibraryWrapper libComp, IntPtr compressor): base(true)
+ {
+ _library = libComp;
+ SetHandle(compressor);
+ }
+
+ ///<inheritdoc/>
+ protected override bool ReleaseHandle() => _library.FreeSafeCompressor(handle) > 0;
+ }
+} \ No newline at end of file
diff --git a/lib/Net.Compression/VNLib.Net.CompressionTests/CompressorManagerTests.cs b/lib/Net.Compression/VNLib.Net.CompressionTests/CompressorManagerTests.cs
index f77b3d2..69672c8 100644
--- a/lib/Net.Compression/VNLib.Net.CompressionTests/CompressorManagerTests.cs
+++ b/lib/Net.Compression/VNLib.Net.CompressionTests/CompressorManagerTests.cs
@@ -7,6 +7,7 @@ using System.IO.Compression;
using System.Security.Cryptography;
using VNLib.Utils.IO;
+using VNLib.Utils.Memory;
using VNLib.Net.Http;
using VNLib.Utils.Extensions;
@@ -18,66 +19,42 @@ namespace VNLib.Net.Compression.Tests
public class CompressorManagerTests
{
const string LIB_PATH = @"../../../../vnlib_compress/build/Debug/vnlib_compress.dll";
-
- [TestMethod()]
- public void CompressDataStreamTest()
+ [TestMethod]
+ public void NativeLibApiTest()
{
- CompressorManager manager = InitCompressorUnderTest();
+ //Load library
+ using NativeCompressionLib lib = NativeCompressionLib.LoadLibrary(LIB_PATH, System.Runtime.InteropServices.DllImportSearchPath.SafeDirectories);
- //Allocate a compressor instance
- object? compressor = manager.AllocCompressor();
+ LibTestComp cp = new(lib, CompressionLevel.Fastest);
- Assert.IsNotNull(compressor);
+ TestSupportedMethods(cp);
- //Test all 3 compression methods
- TestCompressorMethod(manager, compressor, CompressionMethod.Brotli);
- TestCompressorMethod(manager, compressor, CompressionMethod.Gzip);
- TestCompressorMethod(manager, compressor, CompressionMethod.Deflate);
- }
+ //Test for supported methods
+ TestCompressionForSupportedMethods(cp);
+ }
[TestMethod()]
public void InitCompressorTest()
{
CompressorManager manager = InitCompressorUnderTest();
- //Allocate a compressor instance
- object compressor = manager.AllocCompressor();
-
- Assert.IsNotNull(compressor);
-
Assert.ThrowsException<ArgumentNullException>(() => manager.InitCompressor(null!, CompressionMethod.Deflate));
Assert.ThrowsException<ArgumentNullException>(() => manager.DeinitCompressor(null!));
- //Make sure error occurs with non-supported comp
- Assert.ThrowsException<ArgumentException>(() => { manager.InitCompressor(compressor, CompressionMethod.None); });
-
- //test out of range, this should be a native lib error
- Assert.ThrowsException<NotSupportedException>(() => { manager.InitCompressor(compressor, (CompressionMethod)24); });
+ //Allocate a compressor instance
+ object compressor = manager.AllocCompressor();
- //Test all 3 compression methods
- CompressionMethod supported = manager.GetSupportedMethods();
+ Assert.IsNotNull(compressor);
- if ((supported & CompressionMethod.Gzip) > 0)
- {
- //Make sure no error occurs with supported comp
- manager.InitCompressor(compressor, CompressionMethod.Gzip);
- manager.DeinitCompressor(compressor);
- }
+ //Create a new testing wrapper
+ ManagerTestComp cp = new(compressor, manager);
- if((supported & CompressionMethod.Brotli) > 0)
- {
- //Make sure no error occurs with supported comp
- manager.InitCompressor(compressor, CompressionMethod.Brotli);
- manager.DeinitCompressor(compressor);
- }
+ //Test supported methods
+ TestSupportedMethods(cp);
- if((supported & CompressionMethod.Deflate) > 0)
- {
- //Make sure no error occurs with supported comp
- manager.InitCompressor(compressor, CompressionMethod.Deflate);
- manager.DeinitCompressor(compressor);
- }
+ //Test for supported methods
+ TestCompressionForSupportedMethods(cp);
}
private static CompressorManager InitCompressorUnderTest()
@@ -92,12 +69,6 @@ namespace VNLib.Net.Compression.Tests
//Attempt to load the native library
manager.OnLoad(null, doc.RootElement);
- //Get supported methods
- CompressionMethod methods = manager.GetSupportedMethods();
-
- //Verify that at least one method is supported
- Assert.IsFalse(methods == CompressionMethod.None);
-
return manager;
}
@@ -119,8 +90,69 @@ namespace VNLib.Net.Compression.Tests
return Encoding.UTF8.GetString(ms.AsSpan());
}
+
+
+ private static void TestCompressionForSupportedMethods(ITestCompressor testCompressor)
+ {
+ //Get the compressor's supported methods
+ CompressionMethod methods = testCompressor.GetSupportedMethods();
+
+ //Make sure at least on method is supported by the native lib
+ Assert.IsFalse(methods == CompressionMethod.None);
+
+ //Test for brotli support
+ if ((methods & CompressionMethod.Brotli) > 0)
+ {
+ TestCompressorMethod(testCompressor, CompressionMethod.Brotli);
+ }
+
+ //Test for deflate support
+ if ((methods & CompressionMethod.Deflate) > 0)
+ {
+ TestCompressorMethod(testCompressor, CompressionMethod.Deflate);
+ }
+
+ //Test for gzip support
+ if ((methods & CompressionMethod.Gzip) > 0)
+ {
+ TestCompressorMethod(testCompressor, CompressionMethod.Gzip);
+ }
+ }
- private static void TestCompressorMethod(CompressorManager manager, object compressor, CompressionMethod method)
+ private static void TestSupportedMethods(ITestCompressor compressor)
+ {
+ //Make sure error occurs with non-supported comp
+ Assert.ThrowsException<NotSupportedException>(() => { compressor.InitCompressor(CompressionMethod.None); });
+
+ //test out of range, this should be a native lib error
+ Assert.ThrowsException<NotSupportedException>(() => { compressor.InitCompressor((CompressionMethod)24); });
+
+ //Test all 3 compression methods
+ CompressionMethod supported = compressor.GetSupportedMethods();
+
+ if ((supported & CompressionMethod.Gzip) > 0)
+ {
+ //Make sure no error occurs with supported comp
+ compressor.InitCompressor(CompressionMethod.Gzip);
+ compressor.DeinitCompressor();
+ }
+
+ if ((supported & CompressionMethod.Brotli) > 0)
+ {
+ //Make sure no error occurs with supported comp
+ compressor.InitCompressor(CompressionMethod.Brotli);
+ compressor.DeinitCompressor();
+ }
+
+ if ((supported & CompressionMethod.Deflate) > 0)
+ {
+ //Make sure no error occurs with supported comp
+ compressor.InitCompressor(CompressionMethod.Deflate);
+ compressor.DeinitCompressor();
+ }
+ }
+
+ private static void TestCompressorMethod(ITestCompressor compressor, CompressionMethod method)
{
/*
* This test method initalizes a new compressor instance of the desired type
@@ -132,8 +164,12 @@ namespace VNLib.Net.Compression.Tests
*/
//Time to initialize the compressor
- int blockSize = manager.InitCompressor(compressor, method);
+ int blockSize = compressor.InitCompressor(method);
+ /*
+ * Currently not worrying about block size in the native lib, so this
+ * should cause tests to fail when block size is supported later on
+ */
Assert.IsTrue(blockSize == 0);
try
@@ -141,35 +177,32 @@ namespace VNLib.Net.Compression.Tests
using VnMemoryStream outputStream = new();
//Create a buffer to compress
- byte[] buffer = new byte[4096];
+ byte[] buffer = new byte[1024000];
byte[] output = new byte[4096];
//fill with random data
RandomNumberGenerator.Fill(buffer);
- int read = 0;
+ ForwardOnlyMemoryReader<byte> reader = new(buffer);
//try to compress the data in chunks
- while(read < buffer.Length)
+ while(reader.WindowSize > 0)
{
- //Get 4th of a buffer
- ReadOnlyMemory<byte> chunk = buffer.AsMemory(read, 1024);
-
//Compress data
- CompressionResult result = manager.CompressBlock(compressor, chunk, output);
+ CompressionResult result = compressor.CompressBlock(reader.Window, output);
//Write the compressed data to the output stream
- outputStream.Write(output.Slice(0, result.BytesWritten));
+ outputStream.Write(output, 0, result.BytesWritten);
- //Increment the read position
- read += result.BytesRead;
+ //Advance reader
+ reader.Advance(result.BytesRead);
}
//Flush
int flushed = 100;
while(flushed > 0)
{
- flushed = manager.Flush(compressor, output);
+ flushed = compressor.Flush(output);
//Write the compressed data to the output stream
outputStream.Write(output.AsSpan()[0..flushed]);
@@ -183,11 +216,10 @@ namespace VNLib.Net.Compression.Tests
finally
{
//Always deinitialize the compressor when done
- manager.DeinitCompressor(compressor);
+ compressor.DeinitCompressor();
}
}
-
private static byte[] DecompressData(VnMemoryStream inputStream, CompressionMethod method)
{
inputStream.Position = 0;
@@ -214,5 +246,55 @@ namespace VNLib.Net.Compression.Tests
_ => throw new ArgumentException("Unsupported compression method", nameof(method)),
};
}
+
+ interface ITestCompressor
+ {
+ int InitCompressor(CompressionMethod method);
+
+ void DeinitCompressor();
+
+ CompressionResult CompressBlock(ReadOnlyMemory<byte> input, Memory<byte> output);
+
+ int Flush(Memory<byte> buffer);
+
+ CompressionMethod GetSupportedMethods();
+ }
+
+ sealed record class ManagerTestComp(object Compressor, CompressorManager Manager) : ITestCompressor
+ {
+ public CompressionResult CompressBlock(ReadOnlyMemory<byte> input, Memory<byte> output) => Manager.CompressBlock(Compressor, input, output);
+
+ public void DeinitCompressor() => Manager.DeinitCompressor(Compressor);
+
+ public int Flush(Memory<byte> buffer) => Manager.Flush(Compressor, buffer);
+
+ public CompressionMethod GetSupportedMethods() => Manager.GetSupportedMethods();
+
+ public int InitCompressor(CompressionMethod level) => Manager.InitCompressor(Compressor, level);
+
+ }
+
+ sealed record class LibTestComp(NativeCompressionLib Library, CompressionLevel level) : ITestCompressor
+ {
+ private INativeCompressor? _comp;
+
+ public CompressionResult CompressBlock(ReadOnlyMemory<byte> input, Memory<byte> output) => _comp!.Compress(input, output);
+
+ public void DeinitCompressor()
+ {
+ _comp!.Dispose();
+ _comp = null;
+ }
+
+ public int Flush(Memory<byte> buffer) => _comp!.Flush(buffer);
+
+ public CompressionMethod GetSupportedMethods() => Library.GetSupportedMethods();
+
+ public int InitCompressor(CompressionMethod method)
+ {
+ _comp = Library.AllocCompressor(method, level);
+ return _comp.GetBlockSize();
+ }
+ }
}
} \ No newline at end of file
diff --git a/lib/Net.Compression/vnlib_compress/compression.c b/lib/Net.Compression/vnlib_compress/compression.c
index 959540d..1139762 100644
--- a/lib/Net.Compression/vnlib_compress/compression.c
+++ b/lib/Net.Compression/vnlib_compress/compression.c
@@ -220,10 +220,10 @@ VNLIB_EXPORT void* VNLIB_CC AllocateCompressor(CompressorType type, CompressionL
return (void*)result;
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
- #pragma warning(push)
- #pragma warning(disable: 4312)
+#pragma warning(push)
+#pragma warning(disable: 4312)
return (void*)result;
- #pragma warning(pop)
+#pragma warning(pop)
#else
return (void*)result;
#endif
diff --git a/lib/Net.Http/src/Core/Response/ResponseWriter.cs b/lib/Net.Http/src/Core/Response/ResponseWriter.cs
index 97b734a..b6b2488 100644
--- a/lib/Net.Http/src/Core/Response/ResponseWriter.cs
+++ b/lib/Net.Http/src/Core/Response/ResponseWriter.cs
@@ -24,7 +24,7 @@
/*
* This file handles response entity processing. It handles in-memory response
- * processing, as well as stream response processing. It handles contraints
+ * processing, as well as stream response processing. It handles constraints
* such as content-range limits. I tried to eliminate or reduce the amount of
* memory copying required to process the response entity.
*/
@@ -35,7 +35,6 @@ using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
-using VNLib.Utils.IO;
using VNLib.Utils.Memory;
using VNLib.Net.Http.Core.Response;
using VNLib.Net.Http.Core.Compression;
diff --git a/lib/WinRpMalloc/Taskfile.yaml b/lib/WinRpMalloc/Taskfile.yaml
index 90a0898..356db70 100644
--- a/lib/WinRpMalloc/Taskfile.yaml
+++ b/lib/WinRpMalloc/Taskfile.yaml
@@ -17,7 +17,7 @@ tasks:
build:
#build from the src directory
- dir: '{{.USER_WORKING_DIR}}/src'
+ dir: '{{.USER_WORKING_DIR}}'
cmds:
#build in debug mode
- msbuild /p:Configuration=debug {{.BUILD_FLAGS}} {{.MS_ARGS}}