aboutsummaryrefslogtreecommitdiff
path: root/lib/Net.Compression/vnlib_compress
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_compress
parentab07d9d36e3e61f48584920d882d95dead6e7600 (diff)
Native compression lib first build, managed, and tests
Diffstat (limited to 'lib/Net.Compression/vnlib_compress')
-rw-r--r--lib/Net.Compression/vnlib_compress/CMakeLists.txt140
-rw-r--r--lib/Net.Compression/vnlib_compress/compression.c416
-rw-r--r--lib/Net.Compression/vnlib_compress/compression.h161
-rw-r--r--lib/Net.Compression/vnlib_compress/feature_brotli.c199
-rw-r--r--lib/Net.Compression/vnlib_compress/feature_brotli.h46
-rw-r--r--lib/Net.Compression/vnlib_compress/feature_zlib.c268
-rw-r--r--lib/Net.Compression/vnlib_compress/feature_zlib.h50
-rw-r--r--lib/Net.Compression/vnlib_compress/util.h65
-rw-r--r--lib/Net.Compression/vnlib_compress/vnlib_compress.vcxitems30
9 files changed, 1375 insertions, 0 deletions
diff --git a/lib/Net.Compression/vnlib_compress/CMakeLists.txt b/lib/Net.Compression/vnlib_compress/CMakeLists.txt
new file mode 100644
index 0000000..bc3c1df
--- /dev/null
+++ b/lib/Net.Compression/vnlib_compress/CMakeLists.txt
@@ -0,0 +1,140 @@
+cmake_minimum_required(VERSION 3.0)
+
+project(vnlib_compress)
+
+#export header files to the main project
+file(GLOB COMP_HEADERS *.h)
+
+#Add indepednent source files to the project
+set(VNLIB_COMPRESS_SOURCES compression.c)
+
+#add feature specific source files to the project
+if(ENABLE_BROTLI)
+ set(VNLIB_FEATURE_BR_SOURCES feature_brotli.c)
+endif()
+
+if(ENABLE_ZLIB)
+ set(VNLIB_FEATURE_ZLIB_SOURCES feature_zlib.c)
+endif()
+
+#create my shared library
+add_library(${CMAKE_PROJECT_NAME} SHARED ${VNLIB_COMPRESS_SOURCES} ${COMP_HEADERS} ${VNLIB_FEATURE_BR_SOURCES} ${VNLIB_FEATURE_ZLIB_SOURCES})
+
+#Setup the compiler options
+
+enable_language(C)
+set(CMAKE_CXX_STANDARD 90)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+#enable position independent code (for shared libraries with exports)
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+#strict error checking for main project
+set(CMAKE_COMPILE_WARNING_AS_ERROR ON)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+#force the compiler to use the C90/89 standard
+target_compile_features(${CMAKE_PROJECT_NAME} PRIVATE c_std_90)
+
+#setup flags for windows compilation
+if(MSVC)
+
+ #global windows cl flags
+ add_compile_options(
+ /Qspectre
+ /sdl
+ /TC
+ /GS
+
+ #for debug configs
+ $<$<CONFIG:Debug>:/Wall>
+ $<$<CONFIG:Debug>:/options:strict>
+ $<$<CONFIG:Debug>:/FC>
+ $<$<CONFIG:Debug>:/showIncludes>
+ )
+
+ #only target our project
+ target_compile_options(
+ ${CMAKE_PROJECT_NAME}
+ PRIVATE
+
+ $<$<CONFIG:Debug>:/WX> #warnings as errors (only for our project)
+ $<$<CONFIG:Debug>:/Zi>
+ $<$<CONFIG:Debug>:/Zo>
+ )
+
+ #set build macros
+ add_compile_definitions(
+ $<$<CONFIG:DEBUG>:DEBUG>
+ $<$<CONFIG:RELEASE>:RELEASE>
+ )
+
+#configure gcc flags
+elseif(CMAKE_COMPILER_IS_GNUCC)
+
+ add_compile_options(
+ ${CMAKE_PROJECT_NAME}
+ PUBLIC
+ -Wextra
+ -fstack-protector
+
+ $<$<CONFIG:Debug>:-g>
+ $<$<CONFIG:Debug>:-Og>
+ $<$<CONFIG:Debug>:-Wall>
+ $<$<CONFIG:Debug>:-Werror>
+ )
+
+ #only target our project
+ target_compile_options(
+ ${CMAKE_PROJECT_NAME}
+ PRIVATE
+ $<$<CONFIG:Debug>:-pedantic>
+ )
+
+endif()
+
+#check for brotli feature enablement
+if(ENABLE_BROTLI)
+ #add the include directory for brotli so we can include the header files
+ include_directories(../third-party/brotli/c/include)
+
+ #get common sources
+ file(GLOB BROTLI_SOURCES ../third-party/brotli/c/common/*.c)
+
+ #we need to add the brotli encoder source files to the project
+ file(GLOB BROTLI_ENC_SOURCES ../third-party/brotli/c/enc/*.c)
+
+ #add brotli as a static library to link later
+ add_library(lib_brotli STATIC ${BROTLI_SOURCES} ${BROTLI_ENC_SOURCES})
+
+ #define the brotli feature macro to enable brotli support
+ add_definitions(-DVNLIB_COMPRESSOR_BROTLI_ENABLED)
+
+ target_link_libraries(${CMAKE_PROJECT_NAME} lib_brotli)
+endif()
+
+#check for zlib feature enablement
+if(ENABLE_ZLIB)
+ #add the include directory for zlib so we can include the header files
+ include_directories(../third-party/zlib)
+
+ #we only need to add the zlib deflate source files to the project
+ set(ZLIB_SOURCES
+ ../third-party/zlib/deflate.c
+ ../third-party/zlib/adler32.c
+ ../third-party/zlib/adler32_simd.c
+ ../third-party/zlib/crc32.c
+ ../third-party/zlib/zutil.c
+ ../third-party/zlib/trees.c
+ )
+
+ #add zlib as a library to link later
+ add_library(lib_deflate STATIC ${ZLIB_SOURCES})
+
+ #define the zlib feature macro to enable zlib support
+ add_definitions(-DVNLIB_COMPRESSOR_ZLIB_ENABLED)
+
+ target_link_libraries(${CMAKE_PROJECT_NAME} lib_deflate)
+endif()
+
diff --git a/lib/Net.Compression/vnlib_compress/compression.c b/lib/Net.Compression/vnlib_compress/compression.c
new file mode 100644
index 0000000..0e563ff
--- /dev/null
+++ b/lib/Net.Compression/vnlib_compress/compression.c
@@ -0,0 +1,416 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: vnlib_compress
+* File: compression.c
+*
+* vnlib_compress 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_compress 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_compress. If not, see http://www.gnu.org/licenses/.
+*/
+
+
+#include "compression.h"
+
+#ifdef VNLIB_COMPRESSOR_BROTLI_ENABLED
+#include "feature_brotli.h"
+#endif /* VNLIB_COMPRESSOR_BROTLI_ENABLED */
+
+
+#ifdef VNLIB_COMPRESSOR_ZLIB_ENABLED
+#include "feature_zlib.h"
+#endif /* VNLIB_COMPRESSOR_GZIP_ENABLED */
+
+/*
+* Configure DLLMAIN on Windows
+*/
+
+#if false
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+ /*
+ * Taken from the malloc.c file for initializing the library.
+ * and thread events
+ */
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ break;
+ case DLL_THREAD_ATTACH:
+ break;
+ case DLL_THREAD_DETACH:
+ break;
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+#endif /* DLLMAIN */
+
+VNLIB_EXPORT CompressorType VNLIB_CC GetSupportedCompressors(void);
+
+VNLIB_EXPORT int VNLIB_CC GetCompressorBlockSize(void* compressor);
+
+VNLIB_EXPORT CompressorType VNLIB_CC GetCompressorType(void* compressor);
+
+VNLIB_EXPORT CompressionLevel VNLIB_CC GetCompressorLevel(void* compressor);
+
+VNLIB_EXPORT void* VNLIB_CC AllocateCompressor(CompressorType type, CompressionLevel level);
+
+VNLIB_EXPORT int VNLIB_CC FreeCompressor(void* compressor);
+
+VNLIB_EXPORT int VNLIB_CC GetCompressedSize(void* compressor, int inputLength, int flush);
+
+VNLIB_EXPORT int VNLIB_CC CompressBlock(void* compressor, CompressionOperation* operation);
+
+/*
+ Gets the supported compressors, this is defined at compile time and is a convenience method for
+ the user to know what compressors are supported at runtime.
+*/
+VNLIB_EXPORT CompressorType VNLIB_CC GetSupportedCompressors(void)
+{
+ /*
+ * Supported compressors are defined at compile time
+ */
+ CompressorType supported;
+
+ supported = COMP_TYPE_NONE;
+
+#ifdef VNLIB_COMPRESSOR_ZLIB_ENABLED
+ supported |= COMP_TYPE_GZIP;
+ supported |= COMP_TYPE_DEFLATE;
+#endif
+
+#ifdef VNLIB_COMPRESSOR_LZ4_ENABLED
+ supported |= COMP_TYPE_LZ4;
+#endif
+
+#ifdef VNLIB_COMPRESSOR_BROTLI_ENABLED
+ supported |= COMP_TYPE_BROTLI;
+#endif
+
+ return supported;
+}
+
+VNLIB_EXPORT CompressorType VNLIB_CC GetCompressorType(void* compressor)
+{
+ if (!compressor)
+ {
+ return ERR_INVALID_PTR;
+ }
+
+ return ((CompressorState*)compressor)->type;
+}
+
+VNLIB_EXPORT CompressionLevel VNLIB_CC GetCompressorLevel(void* compressor)
+{
+ if (!compressor)
+ {
+ return ERR_INVALID_PTR;
+ }
+
+ return ((CompressorState*)compressor)->level;
+}
+
+VNLIB_EXPORT int VNLIB_CC GetCompressorBlockSize(void* compressor)
+{
+ if (!compressor)
+ {
+ return ERR_INVALID_PTR;
+ }
+
+ return ((CompressorState*)compressor)->blockSize;
+}
+
+
+
+VNLIB_EXPORT void* VNLIB_CC AllocateCompressor(CompressorType type, CompressionLevel level)
+{
+ int result;
+ CompressorState* state;
+
+ /* Validate input arguments */
+ if (level < 0 || level > 9)
+ {
+ return (void*)ERR_COMP_LEVEL_NOT_SUPPORTED;
+ }
+
+ state = (CompressorState*)vncalloc(1, sizeof(CompressorState));
+
+ if (!state)
+ {
+ return (void*)ERR_OUT_OF_MEMORY;
+ }
+
+ /* Configure the comp state */
+ state->type = type;
+ state->level = level;
+
+ result = ERR_COMP_TYPE_NOT_SUPPORTED;
+
+ /*
+ * Compressor types are defined at compile time
+ * and callers are allowed to choose which to allocate
+ */
+
+ switch (type)
+ {
+
+#ifdef VNLIB_COMPRESSOR_BROTLI_ENABLED
+
+ case COMP_TYPE_BROTLI:
+ result = BrAllocCompressor(state);
+ break;
+
+#endif
+
+#ifdef VNLIB_COMPRESSOR_ZLIB_ENABLED
+
+ case COMP_TYPE_DEFLATE:
+ case COMP_TYPE_GZIP:
+ result = DeflateAllocCompressor(state);
+ break;
+#endif
+
+ /*
+ * Unsupported compressor type allow error to propagate
+ */
+ case COMP_TYPE_NONE:
+ default:
+ break;
+ }
+
+
+
+ /*
+ If result was successfull return the context pointer, if
+ the creation failed, free the state if it was allocated
+ and return the error code.
+ */
+
+ if (result > 0)
+ {
+ return (void*)state;
+ }
+ else
+ {
+ vnfree(state);
+
+ /*
+ * Using strict/pedantic error checking int gcc will cause a warning
+ * when casting an int to a void* pointer. We are returning an error code
+ * and it is expected behavior
+ */
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
+ return (void*)result;
+#pragma GCC diagnostic pop
+#elif defined(_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable: 4312)
+ return (void*)result;
+ #pragma warning(pop)
+#else
+ return (void*)result;
+#endif
+ }
+}
+
+VNLIB_EXPORT int VNLIB_CC FreeCompressor(void* compressor)
+{
+ CompressorState* comp;
+ int errorCode;
+
+ if (!compressor)
+ {
+ return ERR_INVALID_PTR;
+ }
+
+ comp = (CompressorState*)compressor;
+ errorCode = TRUE;
+
+ switch (comp->type)
+ {
+
+#ifdef VNLIB_COMPRESSOR_BROTLI_ENABLED
+
+ case COMP_TYPE_BROTLI:
+ BrFreeCompressor(comp);
+ break;
+
+#endif
+
+#ifdef VNLIB_COMPRESSOR_ZLIB_ENABLED
+
+ case COMP_TYPE_DEFLATE:
+ case COMP_TYPE_GZIP:
+
+ /*
+ * Releasing a deflate compressor will cause a deflate
+ * end call, which can fail, we should send the error
+ * to the caller and clean up as best we can.
+ */
+
+ errorCode = DeflateFreeCompressor(comp);
+ break;
+
+#endif
+ /*
+ * If compression type is none, there is nothing to do
+ * since its not technically an error, so just return
+ * true.
+ */
+ case COMP_TYPE_NONE:
+ case COMP_TYPE_LZ4:
+ default:
+ break;
+
+ }
+
+ /*
+ * Free the compressor state
+ */
+
+ vnfree(comp);
+ return errorCode;
+}
+
+VNLIB_EXPORT int VNLIB_CC GetCompressedSize(void* compressor, int inputLength, int flush)
+{
+ CompressorState* comp;
+ int result;
+
+ if (!compressor)
+ {
+ return ERR_INVALID_PTR;
+ }
+
+ comp = (CompressorState*)compressor;
+
+ switch (comp->type)
+ {
+
+#ifdef VNLIB_COMPRESSOR_BROTLI_ENABLED
+
+ case COMP_TYPE_BROTLI:
+ result = BrGetCompressedSize(comp, inputLength);
+ break;
+
+#endif
+
+#ifdef VNLIB_COMPRESSOR_ZLIB_ENABLED
+
+ case COMP_TYPE_DEFLATE:
+ case COMP_TYPE_GZIP:
+ result = DeflateGetCompressedSize(comp, inputLength, flush);
+ break;
+
+#endif
+
+ /*
+ * Set the result as an error code, since the compressor
+ * type is not supported.
+ */
+ case COMP_TYPE_NONE:
+ case COMP_TYPE_LZ4:
+ default:
+ result = ERR_COMP_TYPE_NOT_SUPPORTED;
+ break;
+ }
+
+ return result;
+}
+
+/*
+* Compresses the data contained in the operation structure, ingests and compresses
+* the data then writes it to the output buffer. The result of the operation is
+* returned as an error code. Positive integers indicate success, negative integers
+* indicate failure.
+* @param compressor
+*/
+VNLIB_EXPORT int VNLIB_CC CompressBlock(void* compressor, CompressionOperation* operation)
+{
+ int result;
+ CompressorState* comp;
+
+ comp = (CompressorState*)compressor;
+
+ /*
+ * Validate input arguments
+ */
+
+ if (!comp)
+ {
+ return ERR_INVALID_PTR;
+ }
+
+ if (!operation)
+ {
+ return ERR_INVALID_PTR;
+ }
+
+ /*
+ * Validate buffers, if the buffer length is greate than 0
+ * it must point to a valid buffer
+ */
+
+ if (operation->bytesInLength > 0 && !operation->bytesIn)
+ {
+ return ERR_INVALID_INPUT_DATA;
+ }
+
+ if (operation->bytesOutLength > 0 && !operation->bytesOut)
+ {
+ return ERR_INVALID_OUTPUT_DATA;
+ }
+
+ /*
+ * Determine the compressor type and call the appropriate
+ * compression function
+ */
+
+ switch (comp->type)
+ {
+
+ /* Brolti support */
+#ifdef VNLIB_COMPRESSOR_BROTLI_ENABLED
+
+ case COMP_TYPE_BROTLI:
+ result = BrCompressBlock(comp, operation);
+ break;
+#endif
+
+ /* Deflate support */
+#ifdef VNLIB_COMPRESSOR_ZLIB_ENABLED
+
+ case COMP_TYPE_DEFLATE:
+ case COMP_TYPE_GZIP:
+ result = DeflateCompressBlock(comp, operation);
+ break;
+
+#endif
+
+ case COMP_TYPE_NONE:
+ case COMP_TYPE_LZ4:
+ default:
+ result = ERR_COMP_TYPE_NOT_SUPPORTED;
+ break;
+ }
+
+ return result;
+} \ No newline at end of file
diff --git a/lib/Net.Compression/vnlib_compress/compression.h b/lib/Net.Compression/vnlib_compress/compression.h
new file mode 100644
index 0000000..153b7fc
--- /dev/null
+++ b/lib/Net.Compression/vnlib_compress/compression.h
@@ -0,0 +1,161 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: vnlib_compress
+* File: compression.h
+*
+* vnlib_compress 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_compress 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_compress. If not, see http://www.gnu.org/licenses/.
+*/
+
+
+/*
+* Implementation notes:
+*
+* This library is designed to be a wrapper around the various compression libraries
+* used for dynamic HTTP compression. Is is designed to be exported as a DLL or a shared
+* library and written in portable C code.
+*
+* Compressors are standalone instances created by callers and used to perform compression
+* operations. Compressors are created, used, and destroyed by callers. This library is meant
+* to unify compression to a single api. The goal is performance and portability, so it can
+* be easily used by portable runtimes.
+*/
+
+#pragma once
+
+#ifndef COMPRESSION_H_
+#define COMPRESSION_H_
+
+#include "util.h"
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#define ERR_COMP_TYPE_NOT_SUPPORTED -9
+#define ERR_COMP_LEVEL_NOT_SUPPORTED -10
+#define ERR_INVALID_INPUT_DATA -11
+#define ERR_INVALID_OUTPUT_DATA -12
+
+/*
+* Enumerated list of supported compression types for user selection
+* at runtime.
+*/
+typedef enum CompressorType
+{
+ COMP_TYPE_NONE = 0x00,
+ COMP_TYPE_GZIP = 0x01,
+ COMP_TYPE_DEFLATE = 0x02,
+ COMP_TYPE_BROTLI = 0x04,
+ COMP_TYPE_LZ4 = 0x08
+} CompressorType;
+
+
+/*
+ Specifies values that indicate whether a compression operation emphasizes speed
+ or compression size.
+*/
+typedef enum CompressionLevel
+{
+ /*
+ The compression operation should be optimally compressed, even if the operation
+ takes a longer time to complete.
+ */
+ COMP_LEVEL_OPTIMAL = 0,
+ /*
+ The compression operation should complete as quickly as possible, even if the
+ resulting file is not optimally compressed.
+ */
+ COMP_LEVEL_FASTEST = 1,
+ /*
+ No compression should be performed on the file.
+ */
+ COMP_LEVEL_NO_COMPRESSION = 2,
+ /*
+ The compression operation should create output as small as possible, even if
+ the operation takes a longer time to complete.
+ */
+ COMP_LEVEL_SMALLEST_SIZE = 3
+} CompressionLevel;
+
+
+typedef enum CompressorStatus {
+ COMPRESSOR_STATUS_READY = 0x00,
+ COMPRESSOR_STATUS_INITALIZED = 0x01,
+ COMPRESSOR_STATUS_NEEDS_FLUSH = 0x02
+} CompressorStatus;
+
+typedef struct CompressorStateStruct{
+ /*
+ Indicates the type of underlying compressor.
+ */
+ CompressorType type;
+
+ /*
+ The user specified compression level, the underlying compressor will decide
+ how to handle this value.
+ */
+ CompressionLevel level;
+
+ /*
+ Indicates the suggested block size for the underlying compressor.
+ */
+ int blockSize;
+
+ /*
+ Pointer to the underlying compressor implementation.
+ */
+ void* compressor;
+
+ /*
+ Counts the number of pending bytes since the last successful flush
+ operation.
+ */
+ uint32_t pendingBytes;
+
+} CompressorState;
+
+/*
+* An extern caller generated structure passed to calls for
+* stream compression operations.
+*/
+typedef struct CompressionOperationStruct {
+
+ /*
+ * If the operation is a flush operation
+ */
+ const int flush;
+
+ /*
+ * Input stream data
+ */
+ const uint8_t* bytesIn;
+ const int bytesInLength;
+
+ /*
+ * Output buffer/data stream
+ */
+ uint8_t* bytesOut;
+ const int bytesOutLength;
+
+ /*
+ * Results of the streaming operation
+ */
+
+ int bytesRead;
+ int bytesWritten;
+
+} CompressionOperation;
+
+#endif /* !VNLIB_COMPRESS_MAIN_H_ */ \ No newline at end of file
diff --git a/lib/Net.Compression/vnlib_compress/feature_brotli.c b/lib/Net.Compression/vnlib_compress/feature_brotli.c
new file mode 100644
index 0000000..a8b6fed
--- /dev/null
+++ b/lib/Net.Compression/vnlib_compress/feature_brotli.c
@@ -0,0 +1,199 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: vnlib_compress
+* File: feature_brotli.c
+*
+* vnlib_compress 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_compress 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_compress. If not, see http://www.gnu.org/licenses/.
+*/
+
+#include "feature_brotli.h"
+#include <brotli/encode.h>
+
+#define validateCompState(state) \
+ if (!state) return ERR_INVALID_PTR; \
+ if (!state->compressor) return ERR_BR_INVALID_STATE; \
+
+
+int BrAllocCompressor(CompressorState* state)
+{
+ BrotliEncoderState* comp;
+
+ comp = BrotliEncoderCreateInstance(0, 0, 0);
+
+ if (!comp)
+ {
+ return ERR_BR_INVALID_STATE;
+ }
+
+ state->compressor = comp;
+
+ /*
+ * Setting parameters will only return false if the parameter type is
+ * invalid, or the compressor state is not valid
+ *
+ * Setup some defaults
+ */
+
+ BrotliEncoderSetParameter(comp, BROTLI_PARAM_MODE, BROTLI_MODE_GENERIC);
+ BrotliEncoderSetParameter(comp, BROTLI_PARAM_LGWIN, BR_DEFAULT_WINDOW);
+
+
+ /*
+ * Capture the block size as a size hint if it is greater than 0
+ */
+ if (state->blockSize > 0)
+ {
+ BrotliEncoderSetParameter(comp, BROTLI_PARAM_SIZE_HINT, state->blockSize);
+ }
+
+ /*
+ * Setup compressor quality level based on the requested compression level
+ */
+
+ switch (state->level)
+ {
+
+ case COMP_LEVEL_FASTEST:
+ BrotliEncoderSetParameter(comp, BROTLI_PARAM_QUALITY, BR_COMP_LEVEL_FASTEST);
+ break;
+
+ case COMP_LEVEL_OPTIMAL:
+ BrotliEncoderSetParameter(comp, BROTLI_PARAM_QUALITY, BR_COMP_LEVEL_OPTIMAL);
+ break;
+
+ case COMP_LEVEL_SMALLEST_SIZE:
+ BrotliEncoderSetParameter(comp, BROTLI_PARAM_QUALITY, BR_COMP_LEVEL_SMALLEST_SIZE);
+ break;
+
+ default:
+ BrotliEncoderSetParameter(comp, BROTLI_PARAM_QUALITY, BR_COMP_LEVEL_DEFAULT);
+ break;
+
+ }
+
+ return TRUE;
+}
+
+void BrFreeCompressor(CompressorState* state)
+{
+ /*
+ * Free the compressor instance if it exists
+ */
+ if (state->compressor)
+ {
+ BrotliEncoderDestroyInstance((BrotliEncoderState*)state->compressor);
+ state->compressor = NULL;
+ }
+}
+
+int BrCompressBlock(CompressorState* state, CompressionOperation* operation)
+{
+ BrotliEncoderOperation brOperation;
+ BROTLI_BOOL brResult;
+
+ size_t availableIn, availableOut, totalOut;
+ const uint8_t* nextIn;
+ uint8_t* nextOut;
+
+ /* Validate inputs */
+ validateCompState(state)
+
+ /* Clear the result read / written fields */
+ operation->bytesRead = 0;
+ operation->bytesWritten = 0;
+
+ /*
+ * If the input is empty a flush is not requested, they we are waiting for
+ * more input and this was just an empty call. Should be a no-op
+ */
+
+ if (operation->bytesInLength == 0 && operation->flush < 1)
+ {
+ return TRUE;
+ }
+
+ /*
+ * Determine the operation to perform
+ */
+
+ if (operation->flush)
+ {
+ brOperation = BROTLI_OPERATION_FLUSH;
+ }
+ else
+ {
+ brOperation = BROTLI_OPERATION_PROCESS;
+ }
+
+ /*
+ * Update lengths and data pointers from input/output spans
+ * for stream variables
+ */
+
+ availableIn = operation->bytesInLength;
+ availableOut = operation->bytesOutLength;
+ nextIn = operation->bytesIn;
+ nextOut = operation->bytesOut;
+
+
+ /*
+ * Compress block as stream and store the result
+ * directly on the result output to pass back to the caller
+ */
+
+ brResult = BrotliEncoderCompressStream(
+ state->compressor,
+ brOperation,
+ &availableIn,
+ &nextIn,
+ &availableOut,
+ &nextOut,
+ &totalOut
+ );
+
+ /*
+ * Regardless of the operation success we should return the
+ * results to the caller. Br encoder sets the number of
+ * bytes remaining in the input/output spans
+ */
+
+ operation->bytesRead = operation->bytesInLength - (int)availableIn;
+ operation->bytesWritten = operation->bytesOutLength - (int)availableOut;
+
+ return brResult;
+}
+
+
+int BrGetCompressedSize(CompressorState* state, int length)
+{
+ size_t compressedSize;
+
+ /*
+ * When the flush flag is set, the caller is requesting the
+ * entire size of the compressed data, which can include metadata
+ */
+
+ validateCompState(state)
+
+ if (length <= 0)
+ {
+ return 0;
+ }
+
+ compressedSize = BrotliEncoderMaxCompressedSize(length);
+
+ return (int)compressedSize;
+} \ No newline at end of file
diff --git a/lib/Net.Compression/vnlib_compress/feature_brotli.h b/lib/Net.Compression/vnlib_compress/feature_brotli.h
new file mode 100644
index 0000000..b5a9ed6
--- /dev/null
+++ b/lib/Net.Compression/vnlib_compress/feature_brotli.h
@@ -0,0 +1,46 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: vnlib_compress
+* File: feature_brotli.h
+*
+* vnlib_compress 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_compress 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_compress. If not, see http://www.gnu.org/licenses/.
+*/
+
+#pragma once
+
+#ifndef BROTLI_STUB_H_
+#define BROTLI_STUB_H_
+
+#include "compression.h"
+
+#define ERR_BR_INVALID_STATE -24
+
+#define BR_COMP_LEVEL_FASTEST 1
+#define BR_COMP_LEVEL_OPTIMAL 11
+#define BR_COMP_LEVEL_SMALLEST_SIZE 9
+#define BR_COMP_LEVEL_DEFAULT 5
+
+#define BR_DEFAULT_WINDOW 22
+
+int BrAllocCompressor(CompressorState* state);
+
+void BrFreeCompressor(CompressorState* state);
+
+int BrCompressBlock(CompressorState* state, CompressionOperation* operation);
+
+int BrGetCompressedSize(CompressorState* state, int length);
+
+#endif /* !BROTLI_STUB_H_ */ \ No newline at end of file
diff --git a/lib/Net.Compression/vnlib_compress/feature_zlib.c b/lib/Net.Compression/vnlib_compress/feature_zlib.c
new file mode 100644
index 0000000..210e212
--- /dev/null
+++ b/lib/Net.Compression/vnlib_compress/feature_zlib.c
@@ -0,0 +1,268 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: vnlib_compress
+* File: feature_zlib.c
+*
+* vnlib_compress 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_compress 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_compress. If not, see http://www.gnu.org/licenses/.
+*/
+
+/*
+* Include the stub header and also the zlib header
+*/
+#include "feature_zlib.h"
+#include <zlib.h>
+
+#define validateCompState(state) \
+ if (!state) return ERR_INVALID_PTR; \
+ if (!state->compressor) return ERR_GZ_INVALID_STATE; \
+
+
+int DeflateAllocCompressor(CompressorState* state)
+{
+
+ int result, compLevel;
+ z_stream* stream;
+
+ /*
+ * Allocate the z-stream state on the heap so we can
+ * store it in the compressor state
+ */
+ stream = (z_stream*)vncalloc(1, sizeof(z_stream));
+
+ stream->zalloc = Z_NULL;
+ stream->zfree = Z_NULL;
+ stream->opaque = Z_NULL;
+
+ /*
+ * Initialize the z-stream state with the
+ * desired compression level
+ */
+
+
+ switch (state->level)
+ {
+ case COMP_LEVEL_NO_COMPRESSION:
+ compLevel = Z_NO_COMPRESSION;
+ break;
+
+ case COMP_LEVEL_FASTEST:
+ compLevel = Z_BEST_SPEED;
+ break;
+
+ case COMP_LEVEL_OPTIMAL:
+ compLevel = Z_BEST_COMPRESSION;
+ break;
+
+ case COMP_LEVEL_SMALLEST_SIZE:
+ compLevel = Z_BEST_COMPRESSION;
+ break;
+
+ /*
+ Default compression level
+ */
+ default:
+ compLevel = Z_DEFAULT_COMPRESSION;
+ break;
+ }
+
+ /*
+ * If gzip is enabled, we need to configure the deflatenit2, with
+ * the max window size to 16
+ */
+
+ if(state->type & COMP_TYPE_GZIP)
+ {
+ result = deflateInit2(
+ stream,
+ compLevel,
+ Z_DEFLATED,
+ GZ_ENABLE_GZIP_WINDOW,
+ GZ_DEFAULT_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY
+ );
+ }
+ else
+ {
+ /* Enable raw deflate */
+ result = deflateInit2(
+ stream,
+ compLevel,
+ Z_DEFLATED,
+ GZ_ENABLE_RAW_DEFLATE_WINDOW,
+ GZ_DEFAULT_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY
+ );
+ }
+
+ /*
+ * Inspect the result of the initialization,
+ * of the init failed, free the stream and return
+ * the error code
+ */
+
+ if (result != Z_OK)
+ {
+ vnfree(stream);
+ return result;
+ }
+
+ /*
+ * Assign the z-stream state to the compressor state, all done!
+ */
+ state->compressor = stream;
+ return TRUE;
+}
+
+int DeflateFreeCompressor(CompressorState* state)
+{
+ int result;
+
+ /*
+ * Free the z-stream state, only if the compressor is initialized
+ */
+ if (state->compressor)
+ {
+ /*
+ * Attempt to end the deflate stream, and store the status code
+ */
+
+ result = deflateEnd(state->compressor);
+
+ /*
+ * We can always free the z-stream state, even if the deflate
+ * stream failed to end.
+ */
+
+ vnfree(state->compressor);
+ state->compressor = NULL;
+
+ /*
+ * A data error is acceptable when calling end in this library
+ * since at that point all resources have been cleaned, and zlib
+ * is simply returning a warning that the stream was not properly
+ * terminated.
+ *
+ * We assum that calls to free are meant to clean up resources regarless
+ * of their status
+ */
+ return result == Z_OK || result == Z_DATA_ERROR;
+ }
+
+ return TRUE;
+}
+
+int DeflateCompressBlock(CompressorState* state, CompressionOperation* operation)
+{
+ z_stream* stream;
+ int result;
+
+ validateCompState(state)
+
+ /* Clear the result read/written fields */
+ operation->bytesRead = 0;
+ operation->bytesWritten = 0;
+
+ /*
+ * If the input is empty a flush is not requested, they we are waiting for
+ * more input and this was just an empty call. Should be a no-op
+ */
+
+ if (operation->bytesInLength == 0 && operation->flush < 1)
+ {
+ return TRUE;
+ }
+
+ stream = (z_stream*)state->compressor;
+
+ /*
+ * Overwrite the stream state with the operation parameters from
+ * this next compression operation.
+ *
+ * The caller stores the stream positions in its application memory.
+ */
+ stream->avail_in = operation->bytesInLength;
+ stream->next_in = (Bytef*)operation->bytesIn;
+
+ stream->avail_out = operation->bytesOutLength;
+ stream->next_out = (Bytef*)operation->bytesOut;
+
+ /*
+ * In this library we only use the flush flag as a boolean value.
+ * Callers only set the flush flag when the operation has completed
+ * and the compressor is expected to flush its internal buffers.
+ * (aka finish)
+ */
+
+ result = deflate(stream, operation->flush ? Z_FINISH : Z_NO_FLUSH);
+
+ /*
+ * Regardless of the return value, we should always update the
+ * the number of bytes read and written.
+ *
+ * The result is the number total bytes minus the number of
+ * bytes remaining in the stream.
+ */
+
+ operation->bytesRead = operation->bytesInLength - stream->avail_in;
+ operation->bytesWritten = operation->bytesOutLength - stream->avail_out;
+
+ /*Clear all stream fields after checking results */
+ stream->avail_in = 0;
+ stream->next_in = NULL;
+ stream->avail_out = 0;
+ stream->next_out = NULL;
+
+ return result;
+}
+
+int DeflateGetCompressedSize(CompressorState* state, int length, int flush)
+{
+ uint64_t compressedSize;
+
+ /*
+ * When the flush flag is set, the caller is requesting the
+ * entire size of the compressed data, which can include metadata
+ */
+
+ validateCompState(state)
+
+ if (length <= 0)
+ {
+ return 0;
+ }
+
+ if(flush)
+ {
+ /*
+ * If the flush flag is set, we need to add the size of the
+ * pending data in the stream
+ */
+
+ compressedSize = deflateBound(state->compressor, length + state->pendingBytes);
+ }
+ else
+ {
+ compressedSize = deflateBound(state->compressor, length);
+ }
+
+ /* Verify the results to make sure the value doesnt overflow */
+ if (compressedSize > INT32_MAX)
+ {
+ return ERR_GZ_OVERFLOW;
+ }
+
+ return (int)compressedSize;
+} \ No newline at end of file
diff --git a/lib/Net.Compression/vnlib_compress/feature_zlib.h b/lib/Net.Compression/vnlib_compress/feature_zlib.h
new file mode 100644
index 0000000..0d4b6f6
--- /dev/null
+++ b/lib/Net.Compression/vnlib_compress/feature_zlib.h
@@ -0,0 +1,50 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: vnlib_compress
+* File: feature_zlib.h
+*
+* vnlib_compress 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_compress 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_compress. If not, see http://www.gnu.org/licenses/.
+*/
+
+#pragma once
+
+#ifndef ZLIB_STUB_H_
+#define ZLIB_STUB_H_
+
+#include "compression.h"
+
+#define ERR_GZ_INVALID_STATE -16
+#define ERR_GZ_OVERFLOW -17
+
+/* Allow user to define their own memory level value */
+#ifndef GZ_DEFAULT_MEM_LEVEL
+#define GZ_DEFAULT_MEM_LEVEL 8
+#endif
+
+/* Specifies the window value to enable GZIP */
+#define GZ_ENABLE_GZIP_WINDOW 15 + 16
+#define GZ_ENABLE_RAW_DEFLATE_WINDOW -15
+
+
+int DeflateAllocCompressor(CompressorState* state);
+
+int DeflateFreeCompressor(CompressorState* state);
+
+int DeflateCompressBlock(CompressorState* state, CompressionOperation* operation);
+
+int DeflateGetCompressedSize(CompressorState* state, int length, int flush);
+
+#endif
diff --git a/lib/Net.Compression/vnlib_compress/util.h b/lib/Net.Compression/vnlib_compress/util.h
new file mode 100644
index 0000000..6e7b59e
--- /dev/null
+++ b/lib/Net.Compression/vnlib_compress/util.h
@@ -0,0 +1,65 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: vnlib_compress
+* File: util.h
+*
+* vnlib_compress 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_compress 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_compress. If not, see http://www.gnu.org/licenses/.
+*/
+
+#pragma once
+
+#ifndef UTIL_H_
+#define UTIL_H_
+
+/*
+* Stub missing types and constants for GCC
+*/
+#if defined(__GNUC__)
+#define inline __inline__
+#define VNLIB_EXPORT __attribute__((visibility("default")))
+#define VNLIB_CC
+#elif defined(_MSC_VER)
+#define VNLIB_EXPORT __declspec(dllexport)
+#define VNLIB_CC __cdecl
+#endif /* WIN32 */
+
+#define ERR_INVALID_PTR -1
+#define ERR_OUT_OF_MEMORY -2
+
+#define TRUE 1;
+#define FALSE 0;
+
+#ifndef NULL
+#define NULL 0
+#endif /* !NULL */
+
+/*
+* Stub method for malloc. All calls to vnmalloc should be freed with vnfree.
+*/
+#define vnmalloc(size) malloc(size)
+
+/*
+* Stub method for free
+*/
+#define vnfree(ptr) free(ptr)
+
+/*
+* Stub method for calloc. All calls to vncalloc should be freed with vnfree.
+*/
+#define vncalloc(num, size) calloc(num, size)
+
+
+#endif /* !UTIL_H_ */ \ No newline at end of file
diff --git a/lib/Net.Compression/vnlib_compress/vnlib_compress.vcxitems b/lib/Net.Compression/vnlib_compress/vnlib_compress.vcxitems
new file mode 100644
index 0000000..9249ad9
--- /dev/null
+++ b/lib/Net.Compression/vnlib_compress/vnlib_compress.vcxitems
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup Label="Globals">
+ <MSBuildAllProjects Condition="'$(MSBuildVersion)' == '' Or '$(MSBuildVersion)' &lt; '16.0'">$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
+ <HasSharedItems>true</HasSharedItems>
+ <ItemsProjectGuid>{f2e07583-6244-41a4-84a3-e29fd257ee7c}</ItemsProjectGuid>
+ </PropertyGroup>
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)</AdditionalIncludeDirectories>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectCapability Include="SourceItemsFromImports" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="$(MSBuildThisFileDirectory)feature_brotli.c" />
+ <ClCompile Include="$(MSBuildThisFileDirectory)compression.c" />
+ <ClCompile Include="$(MSBuildThisFileDirectory)feature_zlib.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="$(MSBuildThisFileDirectory)feature_brotli.h" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)compression.h" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)util.h" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)feature_zlib.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <Text Include="$(MSBuildThisFileDirectory)CMakeLists.txt" />
+ </ItemGroup>
+</Project> \ No newline at end of file