aboutsummaryrefslogtreecommitdiff
path: root/lib/Net.Compression
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Net.Compression')
-rw-r--r--lib/Net.Compression/VNLib.Net.Compression/CompressorManager.cs33
-rw-r--r--lib/Net.Compression/VNLib.Net.CompressionTests/CompressorManagerTests.cs46
-rw-r--r--lib/Net.Compression/vnlib_compress/CMakeLists.txt26
-rw-r--r--lib/Net.Compression/vnlib_compress/Taskfile.yaml5
-rw-r--r--lib/Net.Compression/vnlib_compress/compression.c67
-rw-r--r--lib/Net.Compression/vnlib_compress/feature_brotli.c3
-rw-r--r--lib/Net.Compression/vnlib_compress/feature_brotli.h2
-rw-r--r--lib/Net.Compression/vnlib_compress/util.h82
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