diff options
author | vnugent <public@vaughnnugent.com> | 2023-07-27 01:39:44 -0400 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-07-27 01:39:44 -0400 |
commit | 1074db64cc7bae103240ff1220d50d1958b7a900 (patch) | |
tree | a9cff64a3ce836027820b1c536b1a88db4e13a0d /lib/Net.Compression/vnlib_compress | |
parent | ab07d9d36e3e61f48584920d882d95dead6e7600 (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.txt | 140 | ||||
-rw-r--r-- | lib/Net.Compression/vnlib_compress/compression.c | 416 | ||||
-rw-r--r-- | lib/Net.Compression/vnlib_compress/compression.h | 161 | ||||
-rw-r--r-- | lib/Net.Compression/vnlib_compress/feature_brotli.c | 199 | ||||
-rw-r--r-- | lib/Net.Compression/vnlib_compress/feature_brotli.h | 46 | ||||
-rw-r--r-- | lib/Net.Compression/vnlib_compress/feature_zlib.c | 268 | ||||
-rw-r--r-- | lib/Net.Compression/vnlib_compress/feature_zlib.h | 50 | ||||
-rw-r--r-- | lib/Net.Compression/vnlib_compress/util.h | 65 | ||||
-rw-r--r-- | lib/Net.Compression/vnlib_compress/vnlib_compress.vcxitems | 30 |
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)' < '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 |