diff options
Diffstat (limited to 'lib/Net.Compression')
8 files changed, 145 insertions, 119 deletions
diff --git a/lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs b/lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs index a614ab8..efbee77 100644 --- a/lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs +++ b/lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Net.Compression @@ -111,7 +111,8 @@ namespace VNLib.Net.Compression ///<inheritdoc/> public int InitCompressor(object compressorState, CompressionMethod compMethod) { - Compressor compressor = Unsafe.As<Compressor>(compressorState) ?? throw new ArgumentNullException(nameof(compressorState)); + DebugThrowIfNull(compressorState, nameof(compressorState)); + Compressor compressor = Unsafe.As<Compressor>(compressorState); //Instance should be null during initialization calls Debug.Assert(compressor.Instance == IntPtr.Zero, "Init was called but and old compressor instance was not properly freed"); @@ -126,7 +127,8 @@ namespace VNLib.Net.Compression ///<inheritdoc/> public void DeinitCompressor(object compressorState) { - Compressor compressor = Unsafe.As<Compressor>(compressorState) ?? throw new ArgumentNullException(nameof(compressorState)); + DebugThrowIfNull(compressorState, nameof(compressorState)); + Compressor compressor = Unsafe.As<Compressor>(compressorState); if(compressor.Instance == IntPtr.Zero) { @@ -143,7 +145,8 @@ namespace VNLib.Net.Compression ///<inheritdoc/> public int Flush(object compressorState, Memory<byte> output) { - Compressor compressor = Unsafe.As<Compressor>(compressorState) ?? throw new ArgumentNullException(nameof(compressorState)); + DebugThrowIfNull(compressorState, nameof(compressorState)); + Compressor compressor = Unsafe.As<Compressor>(compressorState); if (compressor.Instance == IntPtr.Zero) { @@ -151,14 +154,15 @@ namespace VNLib.Net.Compression } //Force a flush until no more data is available - CompressionResult result = _nativeLib.CompressBlock(compressor.Instance, output, default, true); + CompressionResult result = _nativeLib!.CompressBlock(compressor.Instance, output, default, true); return result.BytesWritten; } ///<inheritdoc/> public CompressionResult CompressBlock(object compressorState, ReadOnlyMemory<byte> input, Memory<byte> output) { - Compressor compressor = Unsafe.As<Compressor>(compressorState) ?? throw new ArgumentNullException(nameof(compressorState)); + DebugThrowIfNull(compressorState, nameof(compressorState)); + Compressor compressor = Unsafe.As<Compressor>(compressorState); if (compressor.Instance == IntPtr.Zero) { @@ -166,9 +170,20 @@ namespace VNLib.Net.Compression } //Compress the block - return _nativeLib.CompressBlock(compressor.Instance, output, input, false); - } - + return _nativeLib!.CompressBlock(compressor.Instance, output, input, false); + } + + /* + * This compressor manager instance is designed to tbe used by a webserver instance, + * (or multiple) as internal calls. We can assume the library compression calls + * are "correct" and should never pass null objects to these function calls. + * + * Its also a managed type and if null will still raise a null ref exception + * if the instances are null. This is just cleaner for debugging purposes. + */ + + [Conditional("DEBUG")] + private static void DebugThrowIfNull<T>(T? obj, string name) => ArgumentNullException.ThrowIfNull(obj, name); /* * A class to contain the compressor state diff --git a/lib/Net.Compression/VNLib.Net.CompressionTests/CompressorManagerTests.cs b/lib/Net.Compression/VNLib.Net.CompressionTests/CompressorManagerTests.cs index 95c1cc1..236d39c 100644 --- a/lib/Net.Compression/VNLib.Net.CompressionTests/CompressorManagerTests.cs +++ b/lib/Net.Compression/VNLib.Net.CompressionTests/CompressorManagerTests.cs @@ -26,7 +26,7 @@ namespace VNLib.Net.Compression.Tests public void NativeLibApiTest() { //Load library - using NativeCompressionLib lib = NativeCompressionLib.LoadLibrary(LIB_PATH, System.Runtime.InteropServices.DllImportSearchPath.SafeDirectories); + using NativeCompressionLib lib = NativeCompressionLib.LoadLibrary(LIB_PATH, DllImportSearchPath.SafeDirectories); LibTestComp cp = new(lib, CompressionLevel.Fastest); @@ -71,7 +71,7 @@ namespace VNLib.Net.Compression.Tests PrintSystemInformation(); //Load native library - using NativeCompressionLib lib = NativeCompressionLib.LoadLibrary(LIB_PATH, System.Runtime.InteropServices.DllImportSearchPath.SafeDirectories); + using NativeCompressionLib lib = NativeCompressionLib.LoadLibrary(LIB_PATH, DllImportSearchPath.SafeDirectories); //Huge array of random data to compress byte[] testData = RandomNumberGenerator.GetBytes(10 * 1024 * 1024); @@ -102,7 +102,7 @@ namespace VNLib.Net.Compression.Tests private static void TestSingleCompressor(LibTestComp comp, CompressionMethod method, CompressionLevel level, byte[] testData) { byte[] outputBlock = new byte[8 * 1024]; - long ms; + long nativeTicks; Stopwatch stopwatch = new (); { @@ -132,11 +132,13 @@ namespace VNLib.Net.Compression.Tests { //Include deinit comp.DeinitCompressor(); - stopwatch.Stop(); - ms = stopwatch.ElapsedTicks / (TimeSpan.TicksPerMillisecond / 1000); + stopwatch.Stop(); } } + nativeTicks = stopwatch.ElapsedTicks; + + //Switch to managed test using (Stream compStream = GetEncodeStream(Stream.Null, method, level)) { stopwatch.Restart(); @@ -151,13 +153,16 @@ namespace VNLib.Net.Compression.Tests } } - long streamMicroseconds = stopwatch.ElapsedTicks / (TimeSpan.TicksPerMillisecond / 1000); + long streamMicroseconds = TicksToMicroseconds(stopwatch.ElapsedTicks); + long nativeMicroseconds = TicksToMicroseconds(nativeTicks); - string winner = ms < streamMicroseconds ? "native" : "stream"; + string winner = nativeMicroseconds < streamMicroseconds ? "native" : "stream"; - Debug.WriteLine($"{method}: {testData.Length} bytes, {ms}misec vs {streamMicroseconds}misec. Winner {winner}"); + Debug.WriteLine($"{method}: {testData.Length} bytes, {nativeMicroseconds}misec vs {streamMicroseconds}misec. Winner {winner}"); } + static long TicksToMicroseconds(long ticks) => ticks / (TimeSpan.TicksPerMillisecond / 1000); + private static CompressorManager InitCompressorUnderTest() { CompressorManager manager = new(); @@ -255,16 +260,17 @@ namespace VNLib.Net.Compression.Tests Debug.WriteLine($"Compressor library supports {supported}"); } + /* + * This test method initalizes a new compressor instance of the desired type + * creates a test data buffer, compresses it using the compressor instance + * then decompresses the compressed data using a managed decompressor as + * a reference and compares the results. + * + * The decompression must be able to recover the original data. + */ + private static void TestCompressorMethod(ITestCompressor compressor, CompressionMethod method) - { - /* - * This test method initalizes a new compressor instance of the desired type - * creates a test data buffer, compresses it using the compressor instance - * then decompresses the compressed data using a managed decompressor as - * a reference and compares the results. - * - * The decompression must be able to recover the original data. - */ + { //Time to initialize the compressor int blockSize = compressor.InitCompressor(method); @@ -311,7 +317,7 @@ namespace VNLib.Net.Compression.Tests outputStream.Write(output.AsSpan()[0..flushed]); } - //Verify the data + //Verify the original data matches the decompressed data byte[] decompressed = DecompressData(outputStream, method); Assert.IsTrue(buffer.SequenceEqual(decompressed)); @@ -393,7 +399,7 @@ Page Size: {Environment.SystemPageSize} CompressionMethod GetSupportedMethods(); } - sealed record class ManagerTestComp(object Compressor, CompressorManager Manager) : ITestCompressor + sealed class ManagerTestComp(object Compressor, CompressorManager Manager) : ITestCompressor { public CompressionResult CompressBlock(ReadOnlyMemory<byte> input, Memory<byte> output) => Manager.CompressBlock(Compressor, input, output); @@ -407,7 +413,7 @@ Page Size: {Environment.SystemPageSize} } - sealed record class LibTestComp(NativeCompressionLib Library, CompressionLevel Level) : ITestCompressor + sealed class LibTestComp(NativeCompressionLib Library, CompressionLevel Level) : ITestCompressor { private INativeCompressor? _comp; diff --git a/lib/Net.Compression/vnlib_compress/CMakeLists.txt b/lib/Net.Compression/vnlib_compress/CMakeLists.txt index 17b01ca..977b9a1 100644 --- a/lib/Net.Compression/vnlib_compress/CMakeLists.txt +++ b/lib/Net.Compression/vnlib_compress/CMakeLists.txt @@ -218,10 +218,16 @@ if(ENABLE_ZLIB) check_c_compiler_flag(/arch:AVX HAS_AVX) if (HAS_AVX) list(APPEND Z_C_FLAGS /arch:AVX) + list(APPEND ZLIB_DEFINITIONS -DHAS_AVX) endif() - elseif(UNIX) + #All x64 machines have SSE2, so we can use it as + #and the Windows compiler will automatically use it + #so we only need to set the definition + list(APPEND Z_C_FLAGS /arch:SSE2) + list(APPEND ZLIB_DEFINITIONS -DHAS_SSE2 -DHAS_SSE42) + elseif(UNIX) #for cloudflare intrinsic detections check_c_compiler_flag(-march=armv8-a+crc ARM_CRC) @@ -229,9 +235,15 @@ if(ENABLE_ZLIB) check_c_compiler_flag(-mssse3 HAS_SSSE3) check_c_compiler_flag(-msse4.2 HAS_SSE42) check_c_compiler_flag(-mpclmul HAS_PCLMUL) - + if(ARM_CRC) list(APPEND Z_C_FLAGS -march=armv8-a+crc) + + if(EXISTS "../third-party/zlib/adler32_simd.c") + list(APPEND ZLIB_DEFINITIONS -DADLER32_SIMD_NEON) + list(APPEND ZLIB_SOURCES ../third-party/zlib/adler32_simd.c) + endif() + else() if(HAS_SSE2) list(APPEND Z_C_FLAGS -msse2) @@ -241,7 +253,7 @@ if(ENABLE_ZLIB) if(HAS_SSSE3) list(APPEND Z_C_FLAGS -mssse3) - #add cloudflare intrinsic optimizations + #add cloudflare intrinsic optimizations, may not be present if using non-cloudflare fork if(EXISTS "../third-party/zlib/adler32_simd.c") list(APPEND ZLIB_DEFINITIONS -DHAS_SSSE3 -DADLER32_SIMD_SSSE3) list(APPEND ZLIB_SOURCES ../third-party/zlib/adler32_simd.c) @@ -255,7 +267,7 @@ if(ENABLE_ZLIB) if(HAS_PCLMUL) list(APPEND Z_C_FLAGS -mpclmul) - #add cloudflare intrinsic optimizations for PCMLONGMUL + #add cloudflare intrinsic optimizations for PCMLONGMUL crc32, may not be present if using non-cloudflare fork if(EXISTS "../third-party/zlib/crc32_simd.c") list(APPEND ZLIB_DEFINITIONS -DHAS_PCLMUL) list(APPEND ZLIB_SOURCES ../third-party/zlib/crc32_simd.c) @@ -263,9 +275,9 @@ if(ENABLE_ZLIB) endif() endif() - - endif() + + #add zlib as a library to link later add_library(lib_deflate STATIC ${ZLIB_SOURCES}) @@ -278,7 +290,7 @@ if(ENABLE_ZLIB) #add the definitions to the zlib project only target_compile_definitions(lib_deflate PRIVATE ${ZLIB_DEFINITIONS}) - #only target zlib project + #only target zlib project with compiler flags target_compile_options(lib_deflate PRIVATE ${Z_C_FLAGS}) target_link_libraries(${CMAKE_PROJECT_NAME} lib_deflate) diff --git a/lib/Net.Compression/vnlib_compress/Taskfile.yaml b/lib/Net.Compression/vnlib_compress/Taskfile.yaml index 5271061..28b3ff0 100644 --- a/lib/Net.Compression/vnlib_compress/Taskfile.yaml +++ b/lib/Net.Compression/vnlib_compress/Taskfile.yaml @@ -21,7 +21,11 @@ tasks: #make third-party dir before cloning libs - cmd: powershell -Command "mkdir '{{.THIRD_PARTY_DIR}}' -Force" + platforms: [windows] ignore_error: true + - cmd: mkdir -p '{{.THIRD_PARTY_DIR}}' + platforms: [linux, darwin] + ignore_error: true - task: zlib - task: brotli @@ -51,7 +55,6 @@ tasks: internal: true status: - cd {{.THIRD_PARTY_DIR}} && git clone https://github.com/cloudflare/zlib.git - cmds: - cd {{.THIRD_PARTY_DIR}}/zlib && git pull diff --git a/lib/Net.Compression/vnlib_compress/compression.c b/lib/Net.Compression/vnlib_compress/compression.c index ffe280b..a414609 100644 --- a/lib/Net.Compression/vnlib_compress/compression.c +++ b/lib/Net.Compression/vnlib_compress/compression.c @@ -130,22 +130,21 @@ VNLIB_EXPORT void* VNLIB_CC AllocateCompressor(CompressorType type, CompressionL switch (type) { + case COMP_TYPE_BROTLI: #ifdef VNLIB_COMPRESSOR_BROTLI_ENABLED - - case COMP_TYPE_BROTLI: result = BrAllocCompressor(state); - break; - #endif -#ifdef VNLIB_COMPRESSOR_ZLIB_ENABLED + break; case COMP_TYPE_DEFLATE: case COMP_TYPE_GZIP: + +#ifdef VNLIB_COMPRESSOR_ZLIB_ENABLED result = DeflateAllocCompressor(state); - break; #endif + break; /* * Unsupported compressor type allow error to propagate @@ -207,30 +206,25 @@ VNLIB_EXPORT int VNLIB_CC FreeCompressor(_In_ void* compressor) switch (comp->type) { - + case COMP_TYPE_BROTLI: #ifdef VNLIB_COMPRESSOR_BROTLI_ENABLED - - case COMP_TYPE_BROTLI: BrFreeCompressor(comp); - break; - #endif - -#ifdef VNLIB_COMPRESSOR_ZLIB_ENABLED + break; case COMP_TYPE_DEFLATE: - case COMP_TYPE_GZIP: - + 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. */ - +#ifdef VNLIB_COMPRESSOR_ZLIB_ENABLED errorCode = DeflateFreeCompressor(comp); +#endif break; -#endif + /* * If compression type is none, there is nothing to do * since its not technically an error, so just return @@ -239,8 +233,7 @@ VNLIB_EXPORT int VNLIB_CC FreeCompressor(_In_ void* compressor) case COMP_TYPE_NONE: case COMP_TYPE_LZ4: default: - break; - + break; } /* @@ -267,26 +260,25 @@ VNLIB_EXPORT int64_t VNLIB_CC GetCompressedSize(_In_ const void* compressor, uin } comp = (CompressorState*)compressor; + result = ERR_COMP_TYPE_NOT_SUPPORTED; switch (comp->type) { -#ifdef VNLIB_COMPRESSOR_BROTLI_ENABLED - case COMP_TYPE_BROTLI: - result = BrGetCompressedSize(comp, inputLength); - break; +#ifdef VNLIB_COMPRESSOR_BROTLI_ENABLED + result = BrGetCompressedSize(comp, inputLength, flush); #endif - -#ifdef VNLIB_COMPRESSOR_ZLIB_ENABLED + break; case COMP_TYPE_DEFLATE: case COMP_TYPE_GZIP: - result = DeflateGetCompressedSize(comp, inputLength, flush); - break; +#ifdef VNLIB_COMPRESSOR_ZLIB_ENABLED + result = DeflateGetCompressedSize(comp, inputLength, flush); #endif + break; /* * Set the result as an error code, since the compressor @@ -294,8 +286,6 @@ VNLIB_EXPORT int64_t VNLIB_CC GetCompressedSize(_In_ const void* compressor, uin */ case COMP_TYPE_NONE: case COMP_TYPE_LZ4: - default: - result = ERR_COMP_TYPE_NOT_SUPPORTED; break; } @@ -350,31 +340,30 @@ VNLIB_EXPORT int VNLIB_CC CompressBlock(_In_ const void* compressor, Compression * compression function */ + result = ERR_COMP_TYPE_NOT_SUPPORTED; + switch (comp->type) { + /* Brolti support */ + case COMP_TYPE_BROTLI: - /* Brolti support */ #ifdef VNLIB_COMPRESSOR_BROTLI_ENABLED - - case COMP_TYPE_BROTLI: result = BrCompressBlock(comp, operation); - break; #endif + break; - /* Deflate support */ -#ifdef VNLIB_COMPRESSOR_ZLIB_ENABLED + /* Deflate support */ case COMP_TYPE_DEFLATE: case COMP_TYPE_GZIP: - result = DeflateCompressBlock(comp, operation); - break; +#ifdef VNLIB_COMPRESSOR_ZLIB_ENABLED + result = DeflateCompressBlock(comp, operation); #endif + break; case COMP_TYPE_NONE: case COMP_TYPE_LZ4: - default: - result = ERR_COMP_TYPE_NOT_SUPPORTED; break; } diff --git a/lib/Net.Compression/vnlib_compress/feature_brotli.c b/lib/Net.Compression/vnlib_compress/feature_brotli.c index 3a3e330..924f1af 100644 --- a/lib/Net.Compression/vnlib_compress/feature_brotli.c +++ b/lib/Net.Compression/vnlib_compress/feature_brotli.c @@ -220,8 +220,9 @@ int BrCompressBlock(const CompressorState* state, CompressionOperation* operatio } -int64_t BrGetCompressedSize(const CompressorState* state, uint64_t length) +int64_t BrGetCompressedSize(const CompressorState* state, uint64_t length, int32_t flush) { + (void)flush; /* * When the flush flag is set, the caller is requesting the * entire size of the compressed data, which can include metadata diff --git a/lib/Net.Compression/vnlib_compress/feature_brotli.h b/lib/Net.Compression/vnlib_compress/feature_brotli.h index b3cb35f..1f2090b 100644 --- a/lib/Net.Compression/vnlib_compress/feature_brotli.h +++ b/lib/Net.Compression/vnlib_compress/feature_brotli.h @@ -41,6 +41,6 @@ void BrFreeCompressor(CompressorState* state); int BrCompressBlock(const CompressorState* state, CompressionOperation* operation); -int64_t BrGetCompressedSize(const CompressorState* state, uint64_t length); +int64_t BrGetCompressedSize(const CompressorState* state, uint64_t length, int32_t flush); #endif /* !BROTLI_STUB_H_ */
\ No newline at end of file diff --git a/lib/Net.Compression/vnlib_compress/util.h b/lib/Net.Compression/vnlib_compress/util.h index 344a28b..8930da7 100644 --- a/lib/Net.Compression/vnlib_compress/util.h +++ b/lib/Net.Compression/vnlib_compress/util.h @@ -30,6 +30,14 @@ * Stub missing types and constants for GCC */ +/* +* If a custom allocator is enabled, use the native heap api +* header and assume linking is enabled +*/ +#ifdef VNLIB_CUSTOM_MALLOC_ENABLE +#include <NativeHeapApi.h> +#endif + #if defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) #define IS_WINDOWS #endif @@ -100,56 +108,48 @@ #define ERR_INVALID_PTR -1 #define ERR_OUT_OF_MEMORY -2 -#if defined(VNLIB_CUSTOM_MALLOC_ENABLE) +#ifdef NATIVE_HEAP_API /* Defined in the NativeHeapApi */ + /* + * Add overrides for malloc, calloc, and free that use + * the nativeheap api to allocate memory + */ -/* -* Add rpmalloc overrides -*/ - -#include <NativeHeapApi.h> + static inline void* vnmalloc(size_t num, size_t size) + { + return heapAlloc(heapGetSharedHeapHandle(), num, size, FALSE); + } + static inline void* vncalloc(size_t num, size_t size) + { + return heapAlloc(heapGetSharedHeapHandle(), num, size, TRUE); + } -/* -* Add overrides for malloc, calloc, and free that use -* the nativeheap api to allocate memory -*/ + static inline void vnfree(void* ptr) + { + ERRNO result; + result = heapFree(heapGetSharedHeapHandle(), ptr); -static inline void* vnmalloc(size_t num, size_t size) -{ - return heapAlloc(heapGetSharedHeapHandle(), num, size, FALSE); -} - -static inline void* vncalloc(size_t num, size_t size) -{ - return heapAlloc(heapGetSharedHeapHandle(), num , size, TRUE); -} - -static inline void vnfree(void* ptr) -{ - ERRNO result; - result = heapFree(heapGetSharedHeapHandle(), ptr); - - //track failed free results - assert(result > 0); -} + //track failed free results + assert(result > 0); + } #else -/* -* Stub method for malloc. All calls to vnmalloc should be freed with vnfree. -*/ -#define vnmalloc(num, size) malloc(num * size) + /* + * Stub method for malloc. All calls to vnmalloc should be freed with vnfree. + */ + #define vnmalloc(num, size) malloc(num * size) -/* -* Stub method for free -*/ -#define vnfree(ptr) free(ptr) + /* + * 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) + /* + * Stub method for calloc. All calls to vncalloc should be freed with vnfree. + */ + #define vncalloc(num, size) calloc(num, size) -#endif +#endif // NATIVE_HEAP_API #endif /* !UTIL_H_ */
\ No newline at end of file |