aboutsummaryrefslogtreecommitdiff
path: root/lib/Net.Compression/VNLib.Net.Compression
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-07-27 01:39:44 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2023-07-27 01:39:44 -0400
commit1074db64cc7bae103240ff1220d50d1958b7a900 (patch)
treea9cff64a3ce836027820b1c536b1a88db4e13a0d /lib/Net.Compression/VNLib.Net.Compression
parentab07d9d36e3e61f48584920d882d95dead6e7600 (diff)
Native compression lib first build, managed, and tests
Diffstat (limited to 'lib/Net.Compression/VNLib.Net.Compression')
-rw-r--r--lib/Net.Compression/VNLib.Net.Compression/CompressionOperation.cs73
-rw-r--r--lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs258
-rw-r--r--lib/Net.Compression/VNLib.Net.Compression/LibraryWrapper.cs277
-rw-r--r--lib/Net.Compression/VNLib.Net.Compression/NativeCompressionException.cs42
-rw-r--r--lib/Net.Compression/VNLib.Net.Compression/ThrowHelper.cs91
-rw-r--r--lib/Net.Compression/VNLib.Net.Compression/VNLib.Net.Compression.csproj37
6 files changed, 778 insertions, 0 deletions
diff --git a/lib/Net.Compression/VNLib.Net.Compression/CompressionOperation.cs b/lib/Net.Compression/VNLib.Net.Compression/CompressionOperation.cs
new file mode 100644
index 0000000..6093e01
--- /dev/null
+++ b/lib/Net.Compression/VNLib.Net.Compression/CompressionOperation.cs
@@ -0,0 +1,73 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Compression
+* File: CompressionOperation.cs
+*
+* CompressorManager.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.Runtime.InteropServices;
+
+namespace VNLib.Net.Compression
+{
+ /// <summary>
+ /// Matches the native compression operation struct
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe ref struct CompressionOperation
+ {
+ #region readonly
+
+ /// <summary>
+ /// A value that indicates a flush operation, 0 for no flush, above 0 for flush
+ /// </summary>
+ public int flush;
+
+ /// <summary>
+ /// A pointer to the input buffer
+ /// </summary>
+ public void* inputBuffer;
+ /// <summary>
+ /// The size of the input buffer
+ /// </summary>
+ public int inputSize;
+
+ /// <summary>
+ /// A pointer to the output buffer
+ /// </summary>
+ public void* outputBuffer;
+ /// <summary>
+ /// The size of the output buffer
+ /// </summary>
+ public int outputSize;
+
+ #endregion
+
+
+ /// <summary>
+ /// An output variable, the number of bytes read from the input buffer
+ /// </summary>
+ public int bytesRead;
+
+ /// <summary>
+ /// An output variable, the number of bytes written to the output buffer
+ /// </summary>
+ public int 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
new file mode 100644
index 0000000..5c4f4fd
--- /dev/null
+++ b/lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs
@@ -0,0 +1,258 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Compression
+* File: CompressorManager.cs
+*
+* CompressorManager.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/.
+*/
+
+/*
+ * Notes:
+ *
+ * This library implements the IHttpCompressorManager for dynamic library
+ * loading by the VNLib.Webserver (or any other application that implements
+ * the IHttpCompressorManager interface).
+ *
+ * It implements an unspecified method called OnLoad that is exepcted to be
+ * called by the VNLib.Webserver during load time. This method is used to
+ * initialize the compressor and load the native library.
+ */
+
+using System;
+using System.Buffers;
+using System.Diagnostics;
+using System.IO.Compression;
+using System.Text.Json;
+using System.Runtime.CompilerServices;
+
+using VNLib.Net.Http;
+using VNLib.Utils.Memory;
+using VNLib.Utils.Logging;
+
+namespace VNLib.Net.Compression
+{
+ public sealed class CompressorManager : IHttpCompressorManager
+ {
+ const string NATIVE_LIB_NAME = "vnlib_compress.dll";
+ const int MIN_BUF_SIZE_DEFAULT = 8192;
+
+ private LibraryWrapper? _nativeLib;
+ private CompressionLevel _compLevel;
+ private int minOutBufferSize;
+
+ /// <summary>
+ /// Called by the VNLib.Webserver during startup to initiialize the compressor.
+ /// </summary>
+ /// <param name="log">The application log provider</param>
+ /// <param name="configJsonString">The raw json configuration data</param>
+ public void OnLoad(ILogProvider? log, JsonElement? config)
+ {
+ _compLevel = CompressionLevel.Optimal;
+ minOutBufferSize = MIN_BUF_SIZE_DEFAULT;
+ string libPath = NATIVE_LIB_NAME;
+
+ if(config.HasValue)
+ {
+ //Get the compression element
+ if(config.Value.TryGetProperty("vnlib.net.compression", out JsonElement compEl))
+ {
+ //Try to get the user specified compression level
+ if(compEl.TryGetProperty("level", out JsonElement lEl))
+ {
+ _compLevel = (CompressionLevel)lEl.GetUInt16();
+ }
+
+ //Allow the user to specify the path to the native library
+ if(compEl.TryGetProperty("lib_path", out JsonElement libEl))
+ {
+ libPath = libEl.GetString() ?? NATIVE_LIB_NAME;
+ }
+
+ if(compEl.TryGetProperty("min_out_buf_size", out JsonElement minBufEl))
+ {
+ minOutBufferSize = minBufEl.GetInt32();
+ }
+ }
+ }
+
+ log?.Debug("Attempting to load native compression library from: {lib}", libPath);
+
+ //Load the native library
+ _nativeLib = LibraryWrapper.LoadLibrary(libPath);
+
+ log?.Debug("Loaded native compression library with compression level {l}", _compLevel.ToString());
+ }
+
+ ///<inheritdoc/>
+ public CompressionMethod GetSupportedMethods()
+ {
+ if(_nativeLib == null)
+ {
+ throw new InvalidOperationException("The native library has not been loaded yet.");
+ }
+
+ return _nativeLib.GetSupportedMethods();
+ }
+
+ ///<inheritdoc/>
+ public object AllocCompressor()
+ {
+ return 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);
+
+ //Alloc the compressor
+ compressor.Instance = _nativeLib!.AllocateCompressor(compMethod, _compLevel);
+
+ //Return the compressor block size
+ return _nativeLib!.GetBlockSize(compressor.Instance);
+ }
+
+ ///<inheritdoc/>
+ public void DeinitCompressor(object compressorState)
+ {
+ Compressor compressor = Unsafe.As<Compressor>(compressorState) ?? throw new ArgumentNullException(nameof(compressorState));
+
+ if(compressor.Instance == IntPtr.Zero)
+ {
+ throw new InvalidOperationException("This compressor instance has not been initialized, cannot free compressor");
+ }
+
+ //Free the output buffer
+ if(compressor.OutputBuffer != null)
+ {
+ ArrayPool<byte>.Shared.Return(compressor.OutputBuffer, true);
+ compressor.OutputBuffer = null;
+ }
+
+ //Free compressor instance
+ _nativeLib!.FreeCompressor(compressor.Instance);
+
+ //Clear pointer after successful free
+ compressor.Instance = IntPtr.Zero;
+ }
+
+ ///<inheritdoc/>
+ public ReadOnlyMemory<byte> Flush(object compressorState)
+ {
+ Compressor compressor = Unsafe.As<Compressor>(compressorState) ?? throw new ArgumentNullException(nameof(compressorState));
+
+ if (compressor.Instance == IntPtr.Zero)
+ {
+ throw new InvalidOperationException("This compressor instance has not been initialized, cannot free compressor");
+ }
+
+ //rent a new buffer of the minimum size if not already allocated
+ compressor.OutputBuffer ??= ArrayPool<byte>.Shared.Rent(minOutBufferSize);
+
+ //Force a flush until no more data is available
+ int bytesWritten = CompressBlock(compressor.Instance, compressor.OutputBuffer, default, true);
+
+ return compressor.OutputBuffer.AsMemory(0, bytesWritten);
+ }
+
+ ///<inheritdoc/>
+ public ReadOnlyMemory<byte> CompressBlock(object compressorState, ReadOnlyMemory<byte> input, bool finalBlock)
+ {
+ Compressor compressor = Unsafe.As<Compressor>(compressorState) ?? throw new ArgumentNullException(nameof(compressorState));
+
+ if (compressor.Instance == IntPtr.Zero)
+ {
+ throw new InvalidOperationException("This compressor instance has not been initialized, cannot free compressor");
+ }
+
+ /*
+ * We only alloc the buffer on the first call because we can assume this is the
+ * largest input data the compressor will see, and the block size should be used
+ * as a reference for callers. If its too small it will just have to be flushed
+ */
+
+ //See if the compressor has a buffer allocated
+ if (compressor.OutputBuffer == null)
+ {
+ //Determine the required buffer size
+ int bufferSize = _nativeLib!.GetOutputSize(compressor.Instance, input.Length, finalBlock ? 1 : 0);
+
+ //clamp the buffer size to the minimum output buffer size
+ bufferSize = Math.Max(bufferSize, minOutBufferSize);
+
+ //rent a new buffer
+ compressor.OutputBuffer = ArrayPool<byte>.Shared.Rent(bufferSize);
+ }
+
+ //Compress the block
+ int bytesWritten = CompressBlock(compressor.Instance, compressor.OutputBuffer, input, finalBlock);
+
+ return compressor.OutputBuffer.AsMemory(0, bytesWritten);
+ }
+
+ private unsafe int CompressBlock(IntPtr comp, byte[] output, ReadOnlyMemory<byte> input, bool finalBlock)
+ {
+ //get pointers to the input and output buffers
+ using MemoryHandle inPtr = input.Pin();
+ using MemoryHandle outPtr = MemoryUtil.PinArrayAndGetHandle(output, 0);
+
+ //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 op->bytesWritten;
+ }
+
+
+ /*
+ * A class to contain the compressor state
+ */
+ private sealed class Compressor
+ {
+ public IntPtr Instance;
+
+ public byte[]? OutputBuffer;
+ }
+
+ }
+} \ 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
new file mode 100644
index 0000000..0f86107
--- /dev/null
+++ b/lib/Net.Compression/VNLib.Net.Compression/LibraryWrapper.cs
@@ -0,0 +1,277 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Compression
+* File: LibraryWrapper.cs
+*
+* LibraryWrapper.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 System.Runtime.CompilerServices;
+
+using VNLib.Utils;
+using VNLib.Utils.Native;
+using VNLib.Utils.Extensions;
+
+using VNLib.Net.Http;
+
+namespace VNLib.Net.Compression
+{
+ /*
+ * Configure the delegate methods for the native library
+ *
+ * All calling conventions are set to Cdecl because the native
+ * library is compiled with Cdecl on all platforms.
+ */
+
+ [SafeMethodName("GetSupportedCompressors")]
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate CompressionMethod GetSupportedMethodsDelegate();
+
+ [SafeMethodName("GetCompressorBlockSize")]
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate int GetBlockSizeDelegate(IntPtr compressor);
+
+ [SafeMethodName("GetCompressorType")]
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate CompressionMethod GetCompressorTypeDelegate(IntPtr compressor);
+
+ [SafeMethodName("GetCompressorLevel")]
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate CompressionLevel GetCompressorLevelDelegate(IntPtr compressor);
+
+ [SafeMethodName("AllocateCompressor")]
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate IntPtr AllocateCompressorDelegate(CompressionMethod type, CompressionLevel level);
+
+ [SafeMethodName("FreeCompressor")]
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate int FreeCompressorDelegate(IntPtr compressor);
+
+ [SafeMethodName("GetCompressedSize")]
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate int GetCompressedSizeDelegate(IntPtr compressor, int uncompressedSize, int flush);
+
+ [SafeMethodName("CompressBlock")]
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ unsafe delegate int CompressBlockDelegate(IntPtr compressor, CompressionOperation* operation);
+
+ /// <summary>
+ /// <para>
+ /// Represents a wrapper that provides access to the native compression library
+ /// specified by a file path.
+ /// </para>
+ /// <para>
+ /// NOTE: This library is not meant to be freed, its meant to be loaded at runtime
+ /// and used for the lifetime of the application.
+ /// </para>
+ /// </summary>
+ internal sealed class LibraryWrapper
+ {
+ private readonly SafeLibraryHandle _lib;
+ private readonly MethodTable _methodTable;
+
+ public string LibFilePath { get; }
+
+ private LibraryWrapper(SafeLibraryHandle lib, string path, in MethodTable methodTable)
+ {
+ _lib = lib;
+ _methodTable = methodTable;
+ LibFilePath = path;
+ }
+
+ /// <summary>
+ /// Loads the native library at the specified path into the current process
+ /// </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)
+ {
+ //Load the library into the current process
+ SafeLibraryHandle lib = SafeLibraryHandle.LoadLibrary(filePath, DllImportSearchPath.SafeDirectories);
+
+ try
+ {
+ //build the method table
+ MethodTable methods = new()
+ {
+ GetMethods = lib.DangerousGetMethod<GetSupportedMethodsDelegate>(),
+
+ GetBlockSize = lib.DangerousGetMethod<GetBlockSizeDelegate>(),
+
+ GetCompType = lib.DangerousGetMethod<GetCompressorTypeDelegate>(),
+
+ GetCompLevel = lib.DangerousGetMethod<GetCompressorLevelDelegate>(),
+
+ Alloc = lib.DangerousGetMethod<AllocateCompressorDelegate>(),
+
+ Free = lib.DangerousGetMethod<FreeCompressorDelegate>(),
+
+ GetOutputSize = lib.DangerousGetMethod<GetCompressedSizeDelegate>(),
+
+ Compress = lib.DangerousGetMethod<CompressBlockDelegate>()
+ };
+
+ return new (lib, filePath, in methods);
+ }
+ catch
+ {
+ lib.Dispose();
+ throw;
+ }
+ }
+
+ /// <summary>
+ /// Gets an enum value of the supported compression methods by the underlying library
+ /// </summary>
+ /// <returns>The supported compression methods</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public CompressionMethod GetSupportedMethods() => _methodTable.GetMethods();
+
+ /// <summary>
+ /// Gets the block size of the specified compressor
+ /// </summary>
+ /// <param name="compressor">A pointer to the compressor instance </param>
+ /// <returns>A integer value of the compressor block size</returns>
+ /// <exception cref="NativeCompressionException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int GetBlockSize(IntPtr compressor)
+ {
+ int result = _methodTable.GetBlockSize(compressor);
+ ThrowHelper.ThrowIfError((ERRNO)result);
+ return result;
+ }
+
+ /// <summary>
+ /// Gets the compressor type of the specified compressor
+ /// </summary>
+ /// <param name="compressor">A pointer to the compressor instance</param>
+ /// <returns>A enum value that represents the compressor type</returns>
+ /// <exception cref="NativeCompressionException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public CompressionMethod GetCompressorType(IntPtr compressor)
+ {
+ CompressionMethod result = _methodTable.GetCompType(compressor);
+ ThrowHelper.ThrowIfError((int)result);
+ return result;
+ }
+
+ /// <summary>
+ /// Gets the compression level of the specified compressor
+ /// </summary>
+ /// <param name="compressor">A pointer to the compressor instance</param>
+ /// <returns>The <see cref="CompressionLevel"/> of the current compressor</returns>
+ /// <exception cref="NativeCompressionException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public CompressionLevel GetCompressorLevel(IntPtr compressor)
+ {
+ CompressionLevel result = _methodTable.GetCompLevel(compressor);
+ ThrowHelper.ThrowIfError((int)result);
+ return result;
+ }
+
+ /// <summary>
+ /// Allocates a new compressor instance of the specified type and compression level
+ /// </summary>
+ /// <param name="type">The compressor type to allocate</param>
+ /// <param name="level">The desired compression level</param>
+ /// <returns>A pointer to the newly allocated compressor instance</returns>
+ /// <exception cref="NotSupportedException"></exception>
+ /// <exception cref="NativeCompressionException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public IntPtr AllocateCompressor(CompressionMethod type, CompressionLevel level)
+ {
+ IntPtr result = _methodTable.Alloc(type, level);
+ ThrowHelper.ThrowIfError(result);
+ return result;
+ }
+
+ /// <summary>
+ /// Frees the specified compressor instance
+ /// </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>
+ /// <exception cref="NativeCompressionException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void FreeCompressor(IntPtr compressor)
+ {
+ int result = _methodTable.Free(compressor);
+ ThrowHelper.ThrowIfError(result);
+ if(result == 0)
+ {
+ throw new NativeCompressionException("Failed to free the compressor instance");
+ }
+ }
+
+ /// <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>
+ /// <param name="inputSize">The size of the input block to compress</param>
+ /// <param name="flush">A value that specifies a flush operation</param>
+ /// <returns>Returns the size of the required output buffer</returns>
+ /// <exception cref="NotSupportedException"></exception>
+ /// <exception cref="NativeCompressionException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int GetOutputSize(IntPtr compressor, int inputSize, int flush)
+ {
+ int result = _methodTable.GetOutputSize(compressor, inputSize, flush);
+ ThrowHelper.ThrowIfError(result);
+ return result;
+ }
+
+ /// <summary>
+ /// Compresses a block of data using the specified compressor instance
+ /// </summary>
+ /// <param name="compressor">The compressor instance used to compress data</param>
+ /// <param name="operation">A pointer to the compression operation structure</param>
+ /// <returns>The result of the operation</returns>
+ /// <exception cref="NotSupportedException"></exception>
+ /// <exception cref="NativeLibraryException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public unsafe int CompressBlock(IntPtr compressor, CompressionOperation* operation)
+ {
+ int result = _methodTable.Compress(compressor, operation);
+ ThrowHelper.ThrowIfError(result);
+ return result;
+ }
+
+ private readonly struct MethodTable
+ {
+ public GetSupportedMethodsDelegate GetMethods { get; init; }
+
+ public GetBlockSizeDelegate GetBlockSize { get; init; }
+
+ public GetCompressorTypeDelegate GetCompType { get; init; }
+
+ public GetCompressorLevelDelegate GetCompLevel { get; init; }
+
+ public AllocateCompressorDelegate Alloc { get; init; }
+
+ public FreeCompressorDelegate Free { get; init; }
+
+ public GetCompressedSizeDelegate GetOutputSize { get; init; }
+
+ public CompressBlockDelegate Compress { get; init; }
+ }
+ }
+} \ No newline at end of file
diff --git a/lib/Net.Compression/VNLib.Net.Compression/NativeCompressionException.cs b/lib/Net.Compression/VNLib.Net.Compression/NativeCompressionException.cs
new file mode 100644
index 0000000..2f523e2
--- /dev/null
+++ b/lib/Net.Compression/VNLib.Net.Compression/NativeCompressionException.cs
@@ -0,0 +1,42 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Compression
+* File: NativeCompressionException.cs
+*
+* NativeCompressionException.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 VNLib.Utils.Native;
+
+namespace VNLib.Net.Compression
+{
+ internal sealed class NativeCompressionException : NativeLibraryException
+ {
+ public NativeCompressionException()
+ { }
+
+ public NativeCompressionException(string message) : base(message)
+ { }
+
+ public NativeCompressionException(string message, Exception innerException) : base(message, innerException)
+ { }
+ }
+} \ No newline at end of file
diff --git a/lib/Net.Compression/VNLib.Net.Compression/ThrowHelper.cs b/lib/Net.Compression/VNLib.Net.Compression/ThrowHelper.cs
new file mode 100644
index 0000000..2793526
--- /dev/null
+++ b/lib/Net.Compression/VNLib.Net.Compression/ThrowHelper.cs
@@ -0,0 +1,91 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Compression
+* File: ThrowHelper.cs
+*
+* ThrowHelper.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 VNLib.Utils;
+
+namespace VNLib.Net.Compression
+{
+ internal static class ThrowHelper
+ {
+ /*
+ * Error codes correspond to constants
+ * in the native compression library
+ */
+ enum NativeErrorType
+ {
+ ErrInvalidPtr = -1,
+ ErrOutOfMemory = -2,
+
+ ErrCompTypeNotSupported = -9,
+ ErrCompLevelNotSupported = -10,
+ ErrInvalidInput = -11,
+ ErrInvalidOutput = -12,
+
+ ErrGzInvalidState = -16,
+ ErrGzOverflow = -17,
+
+ ErrBrInvalidState = -24
+ }
+
+ /// <summary>
+ /// Determines if the specified result is an error and throws an exception if it is
+ /// </summary>
+ /// <param name="result"></param>
+ /// <exception cref="NativeCompressionException"></exception>
+ public static void ThrowIfError(ERRNO result)
+ {
+ //Check for no error
+ if(result > 0)
+ {
+ return;
+ }
+
+ switch ((NativeErrorType)(int)result)
+ {
+ case NativeErrorType.ErrInvalidPtr:
+ throw new NativeCompressionException("A pointer to a compressor instance was null");
+ case NativeErrorType.ErrOutOfMemory:
+ throw new NativeCompressionException("An operation falied because the system is out of memory");
+ case NativeErrorType.ErrCompTypeNotSupported:
+ throw new NotSupportedException("The desired compression method is not supported by the native library");
+ case NativeErrorType.ErrCompLevelNotSupported:
+ throw new NotSupportedException("The desired compression level is not supported by the native library");
+ case NativeErrorType.ErrInvalidInput:
+ throw new NativeCompressionException("The input buffer was null and the input size was greater than 0");
+ case NativeErrorType.ErrInvalidOutput:
+ throw new NativeCompressionException("The output buffer was null and the output size was greater than 0");
+ case NativeErrorType.ErrGzInvalidState:
+ throw new NativeCompressionException("A gzip operation failed because the compressor state is invalid (null compressor pointer)");
+ case NativeErrorType.ErrGzOverflow:
+ throw new NativeCompressionException("A gzip operation failed because the output buffer is too small");
+ case NativeErrorType.ErrBrInvalidState:
+ throw new NativeCompressionException("A brotli operation failed because the compressor state is invalid (null compressor pointer)");
+ default:
+ break;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/lib/Net.Compression/VNLib.Net.Compression/VNLib.Net.Compression.csproj b/lib/Net.Compression/VNLib.Net.Compression/VNLib.Net.Compression.csproj
new file mode 100644
index 0000000..e91c640
--- /dev/null
+++ b/lib/Net.Compression/VNLib.Net.Compression/VNLib.Net.Compression.csproj
@@ -0,0 +1,37 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net6.0</TargetFramework>
+ <RootNamespace>VNLib.Net.Compression</RootNamespace>
+ <AssemblyName>VNLib.Net.Compression</AssemblyName>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <Nullable>enable</Nullable>
+ <GenerateDocumentationFile>True</GenerateDocumentationFile>
+ <AnalysisLevel>latest-all</AnalysisLevel>
+ <RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
+
+ <!--Enable dynamic loading for debugging-->
+ <EnableDynamicLoading>true</EnableDynamicLoading>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <PackageId>VNLib.Net.Compression</PackageId>
+ <Authors>Vaughn Nugent</Authors>
+ <Company>Vaughn Nugent</Company>
+ <Product>VNLib Native Http Compression Provider</Product>
+ <Copyright>Copyright © 2023 Vaughn Nugent</Copyright>
+ <Description>
+ .NET/6.0 dynamically loadable managed wrapper library for loading vnlib_compress native library. It provides
+ an implementation of the IHttpCompressorManager interface for use with the VNLib.Net.Http library and servers
+ wishing to support dynamic response compression.
+ </Description>
+ <PackageProjectUrl>https://www.vaughnnugent.com/resources/software/modules/VNLib.Core</PackageProjectUrl>
+ <RepositoryUrl>https://github.com/VnUgE/VNLib.Core/tree/main/lib/Net.Compression</RepositoryUrl>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\..\Net.Http\src\VNLib.Net.Http.csproj" />
+ <ProjectReference Include="..\..\Utils\src\VNLib.Utils.csproj" />
+ </ItemGroup>
+
+</Project>