From 5865e66c413948c637b4100a4d74c75b2b4e2847 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 19:30:45 -0500 Subject: [PATCH 01/23] Target only net7.0 and drop netstandard2.0/net6.0 support --- .github/workflows/ci.yml | 1 - .github/workflows/release.yml | 1 - bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj | 2 +- src/LibDeflate/LibDeflate.csproj | 2 +- test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj | 2 +- test/LibDeflate.Tests/LibDeflate.Tests.csproj | 2 +- 6 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9668219..59cc3b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,6 @@ jobs: - uses: actions/setup-dotnet@v3 with: dotnet-version: | - 6.0.x 7.0.x - name: Install dependencies run: dotnet restore diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1be9010..af3a9b2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,6 @@ jobs: - uses: actions/setup-dotnet@v3 with: dotnet-version: | - 6.0.x 7.0.x - name: Pack run: dotnet pack -c Release -o pkg diff --git a/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj b/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj index aa69a7d..d6fb02f 100644 --- a/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj +++ b/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net7.0 false diff --git a/src/LibDeflate/LibDeflate.csproj b/src/LibDeflate/LibDeflate.csproj index 4c51334..2f8c5c6 100644 --- a/src/LibDeflate/LibDeflate.csproj +++ b/src/LibDeflate/LibDeflate.csproj @@ -1,7 +1,7 @@  - net6.0;netstandard2.0 + net7.0 LibDeflate.NET 1.18.0 jzebedee diff --git a/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj b/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj index 099dc8f..944dcb6 100644 --- a/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj +++ b/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj @@ -1,7 +1,7 @@  - net6.0;net7.0 + net7.0 false diff --git a/test/LibDeflate.Tests/LibDeflate.Tests.csproj b/test/LibDeflate.Tests/LibDeflate.Tests.csproj index 5adb742..c4eed9f 100644 --- a/test/LibDeflate.Tests/LibDeflate.Tests.csproj +++ b/test/LibDeflate.Tests/LibDeflate.Tests.csproj @@ -1,7 +1,7 @@  - net6.0;net7.0 + net7.0 false latest From 66b1d5cfc6e9512397bcb2b359072a1fac04c574 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 19:31:12 -0500 Subject: [PATCH 02/23] Enable unsafe code --- src/LibDeflate/LibDeflate.csproj | 1 + test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/LibDeflate/LibDeflate.csproj b/src/LibDeflate/LibDeflate.csproj index 2f8c5c6..2b168a5 100644 --- a/src/LibDeflate/LibDeflate.csproj +++ b/src/LibDeflate/LibDeflate.csproj @@ -19,6 +19,7 @@ true true snupkg + true diff --git a/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj b/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj index 944dcb6..7c85972 100644 --- a/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj +++ b/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj @@ -4,6 +4,8 @@ net7.0 false + + True From 4bcb92fdf5f22c0871411c93f11e71e6e79c78a6 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 19:33:02 -0500 Subject: [PATCH 03/23] Replace DllImport with LibraryImport --- src/LibDeflate/Imports/Checksums.cs | 13 +++--- src/LibDeflate/Imports/Compression.cs | 45 +++++++++++-------- .../Imports/CustomMemoryAllocator.cs | 13 +++--- src/LibDeflate/Imports/Decompression.cs | 45 +++++++++++-------- 4 files changed, 70 insertions(+), 46 deletions(-) diff --git a/src/LibDeflate/Imports/Checksums.cs b/src/LibDeflate/Imports/Checksums.cs index d09dfef..275b458 100644 --- a/src/LibDeflate/Imports/Checksums.cs +++ b/src/LibDeflate/Imports/Checksums.cs @@ -1,11 +1,12 @@ using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LibDeflate.Imports; using size_t = System.UIntPtr; -internal static class Checksums +internal static partial class Checksums { /// /// libdeflate_adler32() updates a running Adler-32 checksum with 'len' bytes of @@ -13,8 +14,9 @@ internal static class Checksums /// required initial value for 'adler' is 1. This value is also returned when /// 'buffer' is specified as NULL. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern UInt32 libdeflate_adler32(UInt32 adler, in byte buffer, size_t len); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial UInt32 libdeflate_adler32(UInt32 adler, in byte buffer, size_t len); /// /// libdeflate_crc32() updates a running CRC-32 checksum with 'len' bytes of data @@ -22,6 +24,7 @@ internal static class Checksums /// initial value for 'crc' is 0. This value is also returned when 'buffer' is /// specified as NULL. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern UInt32 libdeflate_crc32(UInt32 crc, in byte buffer, size_t len); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial UInt32 libdeflate_crc32(UInt32 crc, in byte buffer, size_t len); } diff --git a/src/LibDeflate/Imports/Compression.cs b/src/LibDeflate/Imports/Compression.cs index 094b143..ca6907c 100644 --- a/src/LibDeflate/Imports/Compression.cs +++ b/src/LibDeflate/Imports/Compression.cs @@ -1,11 +1,12 @@ -using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace LibDeflate.Imports; using libdeflate_compressor = System.IntPtr; using size_t = System.UIntPtr; -internal static class Compression +internal static partial class Compression { /// /// libdeflate_alloc_compressor() allocates a new compressor that supports @@ -25,8 +26,9 @@ internal static class Compression /// A single compressor is not safe to use by multiple threads concurrently. /// However, different threads may use different compressors concurrently. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern libdeflate_compressor libdeflate_alloc_compressor(int compression_level); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_compressor libdeflate_alloc_compressor(int compression_level); /// /// libdeflate_deflate_compress() performs raw DEFLATE compression on a buffer of @@ -35,8 +37,9 @@ internal static class Compression /// bytes. The return value is the compressed size in bytes, or 0 if the data /// could not be compressed to 'out_nbytes_avail' bytes or fewer. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern size_t libdeflate_deflate_compress(libdeflate_compressor compressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial size_t libdeflate_deflate_compress(libdeflate_compressor compressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail); /// @@ -64,44 +67,50 @@ internal static class Compression /// libdeflate_deflate_compress() returns 0, indicating that the compressed data /// did not fit into the provided output buffer. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern size_t libdeflate_deflate_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial size_t libdeflate_deflate_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); /// /// Like libdeflate_deflate_compress(), but stores the data in the zlib wrapper /// format. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern size_t libdeflate_zlib_compress(libdeflate_compressor compressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial size_t libdeflate_zlib_compress(libdeflate_compressor compressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail); /// /// Like libdeflate_deflate_compress_bound(), but assumes the data will be /// compressed with libdeflate_zlib_compress() rather than with /// libdeflate_deflate_compress(). /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern size_t libdeflate_zlib_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial size_t libdeflate_zlib_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); /// /// Like libdeflate_deflate_compress(), but stores the data in the gzip wrapper /// format. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern size_t libdeflate_gzip_compress(libdeflate_compressor compressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial size_t libdeflate_gzip_compress(libdeflate_compressor compressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail); /// /// Like libdeflate_deflate_compress_bound(), but assumes the data will be /// compressed with libdeflate_gzip_compress() rather than with /// libdeflate_deflate_compress(). /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern size_t libdeflate_gzip_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial size_t libdeflate_gzip_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); /// /// libdeflate_free_compressor() frees a compressor that was allocated with /// libdeflate_alloc_compressor(). If a NULL pointer is passed in, no action is /// taken. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern void libdeflate_free_compressor(libdeflate_compressor compressor); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial void libdeflate_free_compressor(libdeflate_compressor compressor); } diff --git a/src/LibDeflate/Imports/CustomMemoryAllocator.cs b/src/LibDeflate/Imports/CustomMemoryAllocator.cs index 7549b85..86ccbc4 100644 --- a/src/LibDeflate/Imports/CustomMemoryAllocator.cs +++ b/src/LibDeflate/Imports/CustomMemoryAllocator.cs @@ -1,15 +1,16 @@ using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LibDeflate.Imports; using size_t = UIntPtr; -internal static class CustomMemoryAllocator +internal static partial class CustomMemoryAllocator { - //[UnmanagedFunctionPointer(CallingConvention.Cdecl)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate IntPtr malloc_func(size_t size); - //[UnmanagedFunctionPointer(CallingConvention.Cdecl)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void free_func(IntPtr alloc); /// @@ -20,6 +21,8 @@ internal static class CustomMemoryAllocator /// There must not be any libdeflate_compressor or libdeflate_decompressor /// structures in existence when calling this function. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern void libdeflate_set_memory_allocator(malloc_func malloc, free_func free); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial void libdeflate_set_memory_allocator(malloc_func malloc, free_func free); + } diff --git a/src/LibDeflate/Imports/Decompression.cs b/src/LibDeflate/Imports/Decompression.cs index f32a4e3..1f7345c 100644 --- a/src/LibDeflate/Imports/Decompression.cs +++ b/src/LibDeflate/Imports/Decompression.cs @@ -1,11 +1,12 @@ -using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace LibDeflate.Imports; using libdeflate_decompressor = System.IntPtr; using size_t = System.UIntPtr; -internal static class Decompression +internal static partial class Decompression { /// /// Result of a call to libdeflate_deflate_decompress(), @@ -48,8 +49,9 @@ public enum libdeflate_result /// A single decompressor is not safe to use by multiple threads concurrently. /// However, different threads may use different decompressors concurrently. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern libdeflate_decompressor libdeflate_alloc_decompressor(); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_decompressor libdeflate_alloc_decompressor(); /// /// libdeflate_deflate_decompress() decompresses the DEFLATE-compressed stream @@ -83,8 +85,9 @@ public enum libdeflate_result /// not large enough but no other problems were encountered, or another /// nonzero result code if decompression failed for another reason. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern libdeflate_result libdeflate_deflate_decompress(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_out_nbytes_ret); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_result libdeflate_deflate_decompress(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_out_nbytes_ret); /// /// Like libdeflate_deflate_decompress(), but adds the 'actual_in_nbytes_ret' @@ -92,8 +95,9 @@ public enum libdeflate_result /// then the actual compressed size of the DEFLATE stream (aligned to the next /// byte boundary) is written to *actual_in_nbytes_ret. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern libdeflate_result libdeflate_deflate_decompress_ex(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_in_nbytes_ret, out size_t actual_out_nbytes_ret); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_result libdeflate_deflate_decompress_ex(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_in_nbytes_ret, out size_t actual_out_nbytes_ret); /// /// Like libdeflate_deflate_decompress(), but assumes the zlib wrapper format @@ -103,8 +107,9 @@ public enum libdeflate_result /// than 'in_nbytes'. If you need to know exactly where the zlib stream ended, /// use libdeflate_zlib_decompress_ex(). /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern libdeflate_result libdeflate_zlib_decompress(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_out_nbytes_ret); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_result libdeflate_zlib_decompress(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_out_nbytes_ret); /// /// Like libdeflate_deflate_decompress(), but assumes the zlib wrapper format @@ -114,8 +119,9 @@ public enum libdeflate_result /// than 'in_nbytes'. If you need to know exactly where the zlib stream ended, /// use libdeflate_zlib_decompress_ex(). /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern libdeflate_result libdeflate_zlib_decompress_ex(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_in_nbytes_ret, out size_t actual_out_nbytes_ret); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_result libdeflate_zlib_decompress_ex(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_in_nbytes_ret, out size_t actual_out_nbytes_ret); /// /// Like libdeflate_deflate_decompress(), but assumes the gzip wrapper format @@ -125,8 +131,9 @@ public enum libdeflate_result /// will be decompressed. Use libdeflate_gzip_decompress_ex() if you need /// multi-member support. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern libdeflate_result libdeflate_gzip_decompress(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_out_nbytes_ret); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_result libdeflate_gzip_decompress(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_out_nbytes_ret); /// /// Like libdeflate_gzip_decompress(), but adds the 'actual_in_nbytes_ret' @@ -135,14 +142,16 @@ public enum libdeflate_result /// buffer was decompressed), then the actual number of input bytes consumed is /// written to *actual_in_nbytes_ret. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern libdeflate_result libdeflate_gzip_decompress_ex(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_in_nbytes_ret, out size_t actual_out_nbytes_ret); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_result libdeflate_gzip_decompress_ex(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_in_nbytes_ret, out size_t actual_out_nbytes_ret); /// /// libdeflate_free_decompressor() frees a decompressor that was allocated with /// libdeflate_alloc_decompressor(). If a NULL pointer is passed in, no action /// is taken. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern void libdeflate_free_decompressor(libdeflate_decompressor compressor); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial void libdeflate_free_decompressor(libdeflate_decompressor compressor); } \ No newline at end of file From d04096290e975ed3c503fbabcfa2233fa6eb0972 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 19:33:48 -0500 Subject: [PATCH 04/23] Add function pointer overload of libdeflate_set_memory_allocator and tests --- .../Imports/CustomMemoryAllocator.cs | 11 +++++ .../CustomMemoryAllocatorTests.cs | 42 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/LibDeflate/Imports/CustomMemoryAllocator.cs b/src/LibDeflate/Imports/CustomMemoryAllocator.cs index 86ccbc4..992e96f 100644 --- a/src/LibDeflate/Imports/CustomMemoryAllocator.cs +++ b/src/LibDeflate/Imports/CustomMemoryAllocator.cs @@ -25,4 +25,15 @@ internal static partial class CustomMemoryAllocator [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] public static partial void libdeflate_set_memory_allocator(malloc_func malloc, free_func free); + /// + /// Install a custom memory allocator which libdeflate will use for all memory + /// allocations. 'malloc_func' is a function that must behave like malloc(), and + /// 'free_func' is a function that must behave like free(). + /// + /// There must not be any libdeflate_compressor or libdeflate_decompressor + /// structures in existence when calling this function. + /// + [LibraryImport(Constants.DllName, EntryPoint = "libdeflate_set_memory_allocator")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static unsafe partial void libdeflate_set_memory_allocator_unsafe(delegate* unmanaged[Cdecl] malloc, delegate* unmanaged[Cdecl] free); } diff --git a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs index 655810f..496e161 100644 --- a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs +++ b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs @@ -1,4 +1,5 @@ using LibDeflate.Imports; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Xunit; @@ -9,6 +10,15 @@ public class CustomMemoryAllocatorTests private static int mallocCount = 0; private static int freeCount = 0; + //This is not thread-safe, so we disable parallel tests in xunit.runner.json + private static void VerifyAndResetCount() + { + (mallocCount, freeCount) = (0, 0); + + Assert.Equal(0, mallocCount); + Assert.Equal(0, freeCount); + } + //[UnmanagedCallersOnly] private static nint malloc(nuint len) { @@ -26,6 +36,8 @@ private static void free(nint alloc) [Fact] public void UseCustomAllocatorsTest() { + VerifyAndResetCount(); + CustomMemoryAllocator.libdeflate_set_memory_allocator(malloc, free); //allocate something @@ -36,4 +48,34 @@ public void UseCustomAllocatorsTest() Compression.libdeflate_free_compressor(compressor); Assert.Equal(1, freeCount); } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] + private static unsafe void* malloc_unsafe(nuint len) + { + mallocCount++; + return (void*)Marshal.AllocHGlobal((nint)len); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] + private static unsafe void free_unsafe(void* alloc) + { + freeCount++; + Marshal.FreeHGlobal((nint)alloc); + } + + [Fact] + public unsafe void UseCustomAllocatorsUnsafeTest() + { + VerifyAndResetCount(); + + CustomMemoryAllocator.libdeflate_set_memory_allocator_unsafe(&malloc_unsafe, &free_unsafe); + + //allocate something + var compressor = Compression.libdeflate_alloc_compressor(0); + Assert.Equal(1, mallocCount); + + //free something + Compression.libdeflate_free_compressor(compressor); + Assert.Equal(1, freeCount); + } } From 3583793c5beedb676b4fbebd415e70aff43bfb20 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 20:35:44 -0500 Subject: [PATCH 05/23] Make benchmarks a friend of LibDeflate --- src/LibDeflate/Imports/Constants.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/LibDeflate/Imports/Constants.cs b/src/LibDeflate/Imports/Constants.cs index 5986130..a1df3e4 100644 --- a/src/LibDeflate/Imports/Constants.cs +++ b/src/LibDeflate/Imports/Constants.cs @@ -2,6 +2,7 @@ [assembly: InternalsVisibleTo($"{nameof(LibDeflate)}.Tests")] [assembly: InternalsVisibleTo($"{nameof(LibDeflate)}.DangerousTests")] +[assembly: InternalsVisibleTo($"{nameof(LibDeflate)}.Benchmarks")] namespace LibDeflate.Imports; internal static class Constants From 9c327a1a308f11546b57dfbaf823a83bc570a1a0 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 20:36:02 -0500 Subject: [PATCH 06/23] Use NativeMemory for unsafe malloc/free tests --- .../LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs index 496e161..25b498e 100644 --- a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs +++ b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs @@ -19,14 +19,12 @@ private static void VerifyAndResetCount() Assert.Equal(0, freeCount); } - //[UnmanagedCallersOnly] private static nint malloc(nuint len) { mallocCount++; return Marshal.AllocHGlobal((nint)len); } - //[UnmanagedCallersOnly] private static void free(nint alloc) { freeCount++; @@ -53,14 +51,14 @@ public void UseCustomAllocatorsTest() private static unsafe void* malloc_unsafe(nuint len) { mallocCount++; - return (void*)Marshal.AllocHGlobal((nint)len); + return NativeMemory.Alloc(len); } [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] private static unsafe void free_unsafe(void* alloc) { freeCount++; - Marshal.FreeHGlobal((nint)alloc); + NativeMemory.Free(alloc); } [Fact] From d4027101b991b43c09e208244927485a7274e049 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 20:36:26 -0500 Subject: [PATCH 07/23] Use top-level main in benchmarks project --- bench/LibDeflate.Benchmarks/Program.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/bench/LibDeflate.Benchmarks/Program.cs b/bench/LibDeflate.Benchmarks/Program.cs index e1c0549..af5ba4f 100644 --- a/bench/LibDeflate.Benchmarks/Program.cs +++ b/bench/LibDeflate.Benchmarks/Program.cs @@ -1,8 +1,3 @@ using BenchmarkDotNet.Running; -namespace LibDeflate.Benchmarks; - -public class Program -{ - public static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); -} +BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); \ No newline at end of file From 766f50cc56660e2914ec08928acbef7f43b887ad Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 20:36:38 -0500 Subject: [PATCH 08/23] Add custom allocator benchmarks --- .../CustomAllocatorBenchmarks.cs | 57 +++++++++++++++++++ .../LibDeflate.Benchmarks.csproj | 1 + 2 files changed, 58 insertions(+) create mode 100644 bench/LibDeflate.Benchmarks/CustomAllocatorBenchmarks.cs diff --git a/bench/LibDeflate.Benchmarks/CustomAllocatorBenchmarks.cs b/bench/LibDeflate.Benchmarks/CustomAllocatorBenchmarks.cs new file mode 100644 index 0000000..26466c2 --- /dev/null +++ b/bench/LibDeflate.Benchmarks/CustomAllocatorBenchmarks.cs @@ -0,0 +1,57 @@ +using BenchmarkDotNet.Attributes; +using LibDeflate.Imports; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibDeflate.Benchmarks; + +[MemoryDiagnoser] +[SimpleJob] +public class CustomAllocatorBenchmarks +{ + [GlobalSetup(Target = nameof(CompressorAllocCustom))] + public void SetCustomAllocator() + { + Console.WriteLine("Custom Allocator: set"); + CustomMemoryAllocator.libdeflate_set_memory_allocator(malloc, free); + + static nint malloc(nuint len) => Marshal.AllocHGlobal((nint)len); + + static void free(nint alloc) => Marshal.FreeHGlobal(alloc); + } + + [GlobalSetup(Target = nameof(CompressorAllocCustomUnsafe))] + public unsafe void SetCustomAllocatorUnsafe() + { + Console.WriteLine("Custom Unsafe Allocator: set"); + CustomMemoryAllocator.libdeflate_set_memory_allocator_unsafe(&malloc_unsafe, &free_unsafe); + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] + static unsafe void* malloc_unsafe(nuint len) => NativeMemory.Alloc(len); + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] + static unsafe void free_unsafe(void* alloc) => NativeMemory.Free(alloc); + } + + [Benchmark(Baseline = true)] + public void CompressorAlloc() + { + var compressor = Compression.libdeflate_alloc_compressor(0); + Compression.libdeflate_free_compressor(compressor); + } + + [Benchmark] + public void CompressorAllocCustom() + { + var compressor = Compression.libdeflate_alloc_compressor(0); + Compression.libdeflate_free_compressor(compressor); + } + + [Benchmark] + public void CompressorAllocCustomUnsafe() + { + var compressor = Compression.libdeflate_alloc_compressor(0); + Compression.libdeflate_free_compressor(compressor); + } +} diff --git a/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj b/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj index d6fb02f..877c2cc 100644 --- a/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj +++ b/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj @@ -4,6 +4,7 @@ Exe net7.0 false + true From 3724f4c98c537b129ed622fd362e36ec24850d83 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 19:30:45 -0500 Subject: [PATCH 09/23] Target only net7.0 and drop netstandard2.0/net6.0 support --- .github/workflows/ci.yml | 1 - .github/workflows/release.yml | 1 - bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj | 2 +- src/LibDeflate/LibDeflate.csproj | 2 +- test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj | 2 +- test/LibDeflate.Tests/LibDeflate.Tests.csproj | 2 +- 6 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9668219..59cc3b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,6 @@ jobs: - uses: actions/setup-dotnet@v3 with: dotnet-version: | - 6.0.x 7.0.x - name: Install dependencies run: dotnet restore diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1be9010..af3a9b2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,6 @@ jobs: - uses: actions/setup-dotnet@v3 with: dotnet-version: | - 6.0.x 7.0.x - name: Pack run: dotnet pack -c Release -o pkg diff --git a/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj b/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj index 23728b0..faad6cd 100644 --- a/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj +++ b/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net7.0 false diff --git a/src/LibDeflate/LibDeflate.csproj b/src/LibDeflate/LibDeflate.csproj index 815a692..2193bb9 100644 --- a/src/LibDeflate/LibDeflate.csproj +++ b/src/LibDeflate/LibDeflate.csproj @@ -1,7 +1,7 @@  - net6.0;netstandard2.0 + net7.0 LibDeflate.NET 1.19.0 jzebedee diff --git a/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj b/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj index 7fb0d87..f4979e2 100644 --- a/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj +++ b/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj @@ -1,7 +1,7 @@  - net6.0;net7.0 + net7.0 false latest diff --git a/test/LibDeflate.Tests/LibDeflate.Tests.csproj b/test/LibDeflate.Tests/LibDeflate.Tests.csproj index 80c11b0..ddc701d 100644 --- a/test/LibDeflate.Tests/LibDeflate.Tests.csproj +++ b/test/LibDeflate.Tests/LibDeflate.Tests.csproj @@ -4,7 +4,7 @@ - net6.0;net7.0 + net7.0 false latest From 5e1077639d89c817881da65bee8cae6eafa92a0a Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 19:31:12 -0500 Subject: [PATCH 10/23] Enable unsafe code --- src/LibDeflate/LibDeflate.csproj | 1 + test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/LibDeflate/LibDeflate.csproj b/src/LibDeflate/LibDeflate.csproj index 2193bb9..f7c617f 100644 --- a/src/LibDeflate/LibDeflate.csproj +++ b/src/LibDeflate/LibDeflate.csproj @@ -18,6 +18,7 @@ true true snupkg + true diff --git a/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj b/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj index f4979e2..670df7b 100644 --- a/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj +++ b/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj @@ -5,6 +5,8 @@ false latest + + True From 472cfe391b3a4344399f35533005081db1fb69a9 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 19:33:02 -0500 Subject: [PATCH 11/23] Replace DllImport with LibraryImport --- src/LibDeflate/Imports/Checksums.cs | 16 ++++--- src/LibDeflate/Imports/Compression.cs | 45 +++++++++++-------- .../Imports/CustomMemoryAllocator.cs | 15 ++++--- src/LibDeflate/Imports/Decompression.cs | 45 +++++++++++-------- 4 files changed, 73 insertions(+), 48 deletions(-) diff --git a/src/LibDeflate/Imports/Checksums.cs b/src/LibDeflate/Imports/Checksums.cs index 816383f..2659e63 100644 --- a/src/LibDeflate/Imports/Checksums.cs +++ b/src/LibDeflate/Imports/Checksums.cs @@ -1,10 +1,12 @@ -using System.Runtime.InteropServices; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace LibDeflate.Imports; using size_t = nuint; -internal static class Checksums +internal static partial class Checksums { /// /// libdeflate_adler32() updates a running Adler-32 checksum with 'len' bytes of @@ -12,8 +14,9 @@ internal static class Checksums /// required initial value for 'adler' is 1. This value is also returned when /// 'buffer' is specified as NULL. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern uint libdeflate_adler32(uint adler, in byte buffer, size_t len); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial UInt32 libdeflate_adler32(UInt32 adler, in byte buffer, size_t len); /// /// libdeflate_crc32() updates a running CRC-32 checksum with 'len' bytes of data @@ -21,6 +24,7 @@ internal static class Checksums /// initial value for 'crc' is 0. This value is also returned when 'buffer' is /// specified as NULL. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern uint libdeflate_crc32(uint crc, in byte buffer, size_t len); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial UInt32 libdeflate_crc32(UInt32 crc, in byte buffer, size_t len); } diff --git a/src/LibDeflate/Imports/Compression.cs b/src/LibDeflate/Imports/Compression.cs index 53ea83b..aad4902 100644 --- a/src/LibDeflate/Imports/Compression.cs +++ b/src/LibDeflate/Imports/Compression.cs @@ -1,11 +1,12 @@ -using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace LibDeflate.Imports; using libdeflate_compressor = nint; using size_t = nuint; -internal static class Compression +internal static partial class Compression { /// /// libdeflate_alloc_compressor() allocates a new compressor that supports @@ -25,8 +26,9 @@ internal static class Compression /// A single compressor is not safe to use by multiple threads concurrently. /// However, different threads may use different compressors concurrently. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern libdeflate_compressor libdeflate_alloc_compressor(int compression_level); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_compressor libdeflate_alloc_compressor(int compression_level); /// /// Like but allows specifying advanced options per-compressor. @@ -41,8 +43,9 @@ internal static class Compression /// bytes. The return value is the compressed size in bytes, or 0 if the data /// could not be compressed to 'out_nbytes_avail' bytes or fewer. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern size_t libdeflate_deflate_compress(libdeflate_compressor compressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial size_t libdeflate_deflate_compress(libdeflate_compressor compressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail); /// /// libdeflate_deflate_compress_bound() returns a worst-case upper bound on the @@ -69,44 +72,50 @@ internal static class Compression /// libdeflate_deflate_compress() returns 0, indicating that the compressed data /// did not fit into the provided output buffer. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern size_t libdeflate_deflate_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial size_t libdeflate_deflate_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); /// /// Like libdeflate_deflate_compress(), but stores the data in the zlib wrapper /// format. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern size_t libdeflate_zlib_compress(libdeflate_compressor compressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial size_t libdeflate_zlib_compress(libdeflate_compressor compressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail); /// /// Like libdeflate_deflate_compress_bound(), but assumes the data will be /// compressed with libdeflate_zlib_compress() rather than with /// libdeflate_deflate_compress(). /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern size_t libdeflate_zlib_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial size_t libdeflate_zlib_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); /// /// Like libdeflate_deflate_compress(), but stores the data in the gzip wrapper /// format. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern size_t libdeflate_gzip_compress(libdeflate_compressor compressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial size_t libdeflate_gzip_compress(libdeflate_compressor compressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail); /// /// Like libdeflate_deflate_compress_bound(), but assumes the data will be /// compressed with libdeflate_gzip_compress() rather than with /// libdeflate_deflate_compress(). /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern size_t libdeflate_gzip_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial size_t libdeflate_gzip_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); /// /// libdeflate_free_compressor() frees a compressor that was allocated with /// libdeflate_alloc_compressor(). If a NULL pointer is passed in, no action is /// taken. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern void libdeflate_free_compressor(libdeflate_compressor compressor); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial void libdeflate_free_compressor(libdeflate_compressor compressor); } diff --git a/src/LibDeflate/Imports/CustomMemoryAllocator.cs b/src/LibDeflate/Imports/CustomMemoryAllocator.cs index e48b9a2..86ccbc4 100644 --- a/src/LibDeflate/Imports/CustomMemoryAllocator.cs +++ b/src/LibDeflate/Imports/CustomMemoryAllocator.cs @@ -1,15 +1,16 @@ using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LibDeflate.Imports; using size_t = UIntPtr; -internal static class CustomMemoryAllocator +internal static partial class CustomMemoryAllocator { - //[UnmanagedFunctionPointer(CallingConvention.Cdecl)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate IntPtr malloc_func(size_t size); - //[UnmanagedFunctionPointer(CallingConvention.Cdecl)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void free_func(IntPtr alloc); /// @@ -20,6 +21,8 @@ internal static class CustomMemoryAllocator /// There must not be any libdeflate_compressor or libdeflate_decompressor /// structures in existence when calling this function. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern void libdeflate_set_memory_allocator(malloc_func malloc, free_func free); -} \ No newline at end of file + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial void libdeflate_set_memory_allocator(malloc_func malloc, free_func free); + +} diff --git a/src/LibDeflate/Imports/Decompression.cs b/src/LibDeflate/Imports/Decompression.cs index bd3f380..9b08150 100644 --- a/src/LibDeflate/Imports/Decompression.cs +++ b/src/LibDeflate/Imports/Decompression.cs @@ -1,11 +1,12 @@ -using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace LibDeflate.Imports; using libdeflate_decompressor = System.IntPtr; using size_t = System.UIntPtr; -internal static class Decompression +internal static partial class Decompression { /// /// Result of a call to libdeflate_deflate_decompress(), @@ -48,8 +49,9 @@ public enum libdeflate_result /// A single decompressor is not safe to use by multiple threads concurrently. /// However, different threads may use different decompressors concurrently. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern libdeflate_decompressor libdeflate_alloc_decompressor(); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_decompressor libdeflate_alloc_decompressor(); /// /// Like but allows specifying advanced options per-decompressor. @@ -89,8 +91,9 @@ public enum libdeflate_result /// not large enough but no other problems were encountered, or another /// nonzero result code if decompression failed for another reason. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern libdeflate_result libdeflate_deflate_decompress(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_out_nbytes_ret); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_result libdeflate_deflate_decompress(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_out_nbytes_ret); /// /// Like libdeflate_deflate_decompress(), but adds the 'actual_in_nbytes_ret' @@ -98,8 +101,9 @@ public enum libdeflate_result /// then the actual compressed size of the DEFLATE stream (aligned to the next /// byte boundary) is written to *actual_in_nbytes_ret. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern libdeflate_result libdeflate_deflate_decompress_ex(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_in_nbytes_ret, out size_t actual_out_nbytes_ret); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_result libdeflate_deflate_decompress_ex(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_in_nbytes_ret, out size_t actual_out_nbytes_ret); /// /// Like libdeflate_deflate_decompress(), but assumes the zlib wrapper format @@ -109,8 +113,9 @@ public enum libdeflate_result /// than 'in_nbytes'. If you need to know exactly where the zlib stream ended, /// use libdeflate_zlib_decompress_ex(). /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern libdeflate_result libdeflate_zlib_decompress(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_out_nbytes_ret); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_result libdeflate_zlib_decompress(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_out_nbytes_ret); /// /// Like libdeflate_deflate_decompress(), but assumes the zlib wrapper format @@ -120,8 +125,9 @@ public enum libdeflate_result /// than 'in_nbytes'. If you need to know exactly where the zlib stream ended, /// use libdeflate_zlib_decompress_ex(). /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern libdeflate_result libdeflate_zlib_decompress_ex(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_in_nbytes_ret, out size_t actual_out_nbytes_ret); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_result libdeflate_zlib_decompress_ex(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_in_nbytes_ret, out size_t actual_out_nbytes_ret); /// /// Like libdeflate_deflate_decompress(), but assumes the gzip wrapper format @@ -131,8 +137,9 @@ public enum libdeflate_result /// will be decompressed. Use libdeflate_gzip_decompress_ex() if you need /// multi-member support. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern libdeflate_result libdeflate_gzip_decompress(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_out_nbytes_ret); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_result libdeflate_gzip_decompress(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_out_nbytes_ret); /// /// Like libdeflate_gzip_decompress(), but adds the 'actual_in_nbytes_ret' @@ -141,14 +148,16 @@ public enum libdeflate_result /// buffer was decompressed), then the actual number of input bytes consumed is /// written to *actual_in_nbytes_ret. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern libdeflate_result libdeflate_gzip_decompress_ex(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_in_nbytes_ret, out size_t actual_out_nbytes_ret); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_result libdeflate_gzip_decompress_ex(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_in_nbytes_ret, out size_t actual_out_nbytes_ret); /// /// libdeflate_free_decompressor() frees a decompressor that was allocated with /// libdeflate_alloc_decompressor(). If a NULL pointer is passed in, no action /// is taken. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern void libdeflate_free_decompressor(libdeflate_decompressor decompressor); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial void libdeflate_free_decompressor(libdeflate_decompressor compressor); } \ No newline at end of file From f578c88a193b73be74e5637b33ea3d781099f439 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 19:33:48 -0500 Subject: [PATCH 12/23] Add function pointer overload of libdeflate_set_memory_allocator and tests --- .../Imports/CustomMemoryAllocator.cs | 11 +++++ .../CustomMemoryAllocatorTests.cs | 42 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/LibDeflate/Imports/CustomMemoryAllocator.cs b/src/LibDeflate/Imports/CustomMemoryAllocator.cs index 86ccbc4..992e96f 100644 --- a/src/LibDeflate/Imports/CustomMemoryAllocator.cs +++ b/src/LibDeflate/Imports/CustomMemoryAllocator.cs @@ -25,4 +25,15 @@ internal static partial class CustomMemoryAllocator [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] public static partial void libdeflate_set_memory_allocator(malloc_func malloc, free_func free); + /// + /// Install a custom memory allocator which libdeflate will use for all memory + /// allocations. 'malloc_func' is a function that must behave like malloc(), and + /// 'free_func' is a function that must behave like free(). + /// + /// There must not be any libdeflate_compressor or libdeflate_decompressor + /// structures in existence when calling this function. + /// + [LibraryImport(Constants.DllName, EntryPoint = "libdeflate_set_memory_allocator")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static unsafe partial void libdeflate_set_memory_allocator_unsafe(delegate* unmanaged[Cdecl] malloc, delegate* unmanaged[Cdecl] free); } diff --git a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs index 2f2f2d8..2f2c879 100644 --- a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs +++ b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs @@ -1,4 +1,5 @@ using LibDeflate.Imports; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Xunit; @@ -9,6 +10,15 @@ public class CustomMemoryAllocatorTests private static int mallocCount = 0; private static int freeCount = 0; + //This is not thread-safe, so we disable parallel tests in xunit.runner.json + private static void VerifyAndResetCount() + { + (mallocCount, freeCount) = (0, 0); + + Assert.Equal(0, mallocCount); + Assert.Equal(0, freeCount); + } + //[UnmanagedCallersOnly] private static nint malloc(nuint len) { @@ -26,6 +36,8 @@ private static void free(nint alloc) [Fact] public void UseGlobalCustomAllocatorsTest() { + VerifyAndResetCount(); + CustomMemoryAllocator.libdeflate_set_memory_allocator(malloc, free); //test compressor @@ -89,4 +101,34 @@ public void UsePerCompressorCustomAllocatorsTest() Assert.Equal(startingGlobalFrees, freeCount); } } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] + private static unsafe void* malloc_unsafe(nuint len) + { + mallocCount++; + return (void*)Marshal.AllocHGlobal((nint)len); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] + private static unsafe void free_unsafe(void* alloc) + { + freeCount++; + Marshal.FreeHGlobal((nint)alloc); + } + + [Fact] + public unsafe void UseCustomAllocatorsUnsafeTest() + { + VerifyAndResetCount(); + + CustomMemoryAllocator.libdeflate_set_memory_allocator_unsafe(&malloc_unsafe, &free_unsafe); + + //allocate something + var compressor = Compression.libdeflate_alloc_compressor(0); + Assert.Equal(1, mallocCount); + + //free something + Compression.libdeflate_free_compressor(compressor); + Assert.Equal(1, freeCount); + } } From 9a8da23ed17701e0a33074ffa0d7c0cc74f4db76 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 20:35:44 -0500 Subject: [PATCH 13/23] Make benchmarks a friend of LibDeflate --- src/LibDeflate/Imports/Constants.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/LibDeflate/Imports/Constants.cs b/src/LibDeflate/Imports/Constants.cs index 907d10b..9cfcc19 100644 --- a/src/LibDeflate/Imports/Constants.cs +++ b/src/LibDeflate/Imports/Constants.cs @@ -3,6 +3,7 @@ [assembly: InternalsVisibleTo($"{nameof(LibDeflate)}.Tests")] [assembly: InternalsVisibleTo($"{nameof(LibDeflate)}.DangerousTests")] +[assembly: InternalsVisibleTo($"{nameof(LibDeflate)}.Benchmarks")] namespace LibDeflate.Imports; internal static class Constants From f6c43c99aadb7d72d0371f836b94c32195ce77d0 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 20:36:02 -0500 Subject: [PATCH 14/23] Use NativeMemory for unsafe malloc/free tests --- .../LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs index 2f2c879..4faf3e6 100644 --- a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs +++ b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs @@ -19,14 +19,12 @@ private static void VerifyAndResetCount() Assert.Equal(0, freeCount); } - //[UnmanagedCallersOnly] private static nint malloc(nuint len) { mallocCount++; return Marshal.AllocHGlobal((nint)len); } - //[UnmanagedCallersOnly] private static void free(nint alloc) { freeCount++; @@ -106,14 +104,14 @@ public void UsePerCompressorCustomAllocatorsTest() private static unsafe void* malloc_unsafe(nuint len) { mallocCount++; - return (void*)Marshal.AllocHGlobal((nint)len); + return NativeMemory.Alloc(len); } [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] private static unsafe void free_unsafe(void* alloc) { freeCount++; - Marshal.FreeHGlobal((nint)alloc); + NativeMemory.Free(alloc); } [Fact] From f86323615223f1a445edff14fe7ee633c9622157 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 20:36:26 -0500 Subject: [PATCH 15/23] Use top-level main in benchmarks project --- bench/LibDeflate.Benchmarks/Program.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/bench/LibDeflate.Benchmarks/Program.cs b/bench/LibDeflate.Benchmarks/Program.cs index e1c0549..af5ba4f 100644 --- a/bench/LibDeflate.Benchmarks/Program.cs +++ b/bench/LibDeflate.Benchmarks/Program.cs @@ -1,8 +1,3 @@ using BenchmarkDotNet.Running; -namespace LibDeflate.Benchmarks; - -public class Program -{ - public static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); -} +BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); \ No newline at end of file From aa984d373e55fb7d5ba0167a29f4a46ca47a2049 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 20:36:38 -0500 Subject: [PATCH 16/23] Add custom allocator benchmarks --- .../CustomAllocatorBenchmarks.cs | 57 +++++++++++++++++++ .../LibDeflate.Benchmarks.csproj | 1 + 2 files changed, 58 insertions(+) create mode 100644 bench/LibDeflate.Benchmarks/CustomAllocatorBenchmarks.cs diff --git a/bench/LibDeflate.Benchmarks/CustomAllocatorBenchmarks.cs b/bench/LibDeflate.Benchmarks/CustomAllocatorBenchmarks.cs new file mode 100644 index 0000000..26466c2 --- /dev/null +++ b/bench/LibDeflate.Benchmarks/CustomAllocatorBenchmarks.cs @@ -0,0 +1,57 @@ +using BenchmarkDotNet.Attributes; +using LibDeflate.Imports; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibDeflate.Benchmarks; + +[MemoryDiagnoser] +[SimpleJob] +public class CustomAllocatorBenchmarks +{ + [GlobalSetup(Target = nameof(CompressorAllocCustom))] + public void SetCustomAllocator() + { + Console.WriteLine("Custom Allocator: set"); + CustomMemoryAllocator.libdeflate_set_memory_allocator(malloc, free); + + static nint malloc(nuint len) => Marshal.AllocHGlobal((nint)len); + + static void free(nint alloc) => Marshal.FreeHGlobal(alloc); + } + + [GlobalSetup(Target = nameof(CompressorAllocCustomUnsafe))] + public unsafe void SetCustomAllocatorUnsafe() + { + Console.WriteLine("Custom Unsafe Allocator: set"); + CustomMemoryAllocator.libdeflate_set_memory_allocator_unsafe(&malloc_unsafe, &free_unsafe); + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] + static unsafe void* malloc_unsafe(nuint len) => NativeMemory.Alloc(len); + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] + static unsafe void free_unsafe(void* alloc) => NativeMemory.Free(alloc); + } + + [Benchmark(Baseline = true)] + public void CompressorAlloc() + { + var compressor = Compression.libdeflate_alloc_compressor(0); + Compression.libdeflate_free_compressor(compressor); + } + + [Benchmark] + public void CompressorAllocCustom() + { + var compressor = Compression.libdeflate_alloc_compressor(0); + Compression.libdeflate_free_compressor(compressor); + } + + [Benchmark] + public void CompressorAllocCustomUnsafe() + { + var compressor = Compression.libdeflate_alloc_compressor(0); + Compression.libdeflate_free_compressor(compressor); + } +} diff --git a/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj b/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj index faad6cd..5c710c1 100644 --- a/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj +++ b/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj @@ -4,6 +4,7 @@ Exe net7.0 false + true From b58827bf4458bce5d3a9f794c3aa81f4847c5c09 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sat, 20 May 2023 13:36:37 -0500 Subject: [PATCH 17/23] Add broken benchmarks See: https://github.com/dotnet/BenchmarkDotNet/issues/1408 --- .../DeflateCompressorBenchmarks.cs | 84 ++++++++++++++----- 1 file changed, 63 insertions(+), 21 deletions(-) diff --git a/bench/LibDeflate.Benchmarks/DeflateCompressorBenchmarks.cs b/bench/LibDeflate.Benchmarks/DeflateCompressorBenchmarks.cs index 957c13d..b47dcd3 100644 --- a/bench/LibDeflate.Benchmarks/DeflateCompressorBenchmarks.cs +++ b/bench/LibDeflate.Benchmarks/DeflateCompressorBenchmarks.cs @@ -4,6 +4,7 @@ using System.IO; using System.IO.Compression; using System.Linq; +using System.Runtime.CompilerServices; namespace LibDeflate.Benchmarks; @@ -11,49 +12,90 @@ namespace LibDeflate.Benchmarks; [SimpleJob] public class DeflateCompressorBenchmarks { - public static IEnumerable Inputs => from key in TestFiles.Keys - from level in Levels - select new object[] { key, level }; + private static string AssetBase + { + get + { + var cwd = Directory.GetCurrentDirectory(); - private static Dictionary TestFiles { get; } = Directory.EnumerateFiles(@"texts/") - .Where(fn => !Path.GetExtension(fn).Equals(".gz", StringComparison.OrdinalIgnoreCase)) - .ToDictionary(fn => Path.GetFileNameWithoutExtension(fn), File.ReadAllBytes); + string assetsDir; + while (!Directory.Exists(assetsDir = Path.Join(cwd, "assets"))) + { + cwd = Path.GetDirectoryName(cwd); + } + + return assetsDir; + } + } private static IEnumerable Levels { get { - //yield return 0; + yield return 0; yield return 1; - //yield return 6; - //yield return 9; + yield return -1; + yield return 9; } } - [Benchmark(Baseline = true)] - [ArgumentsSource(nameof(Inputs))] - public void DeflateSIO(string testFile, int level) + [GlobalSetup] + public static void PrepareTestAssets() { - var compressionLevel = level switch + var assetsFolder = Path.Join(AssetBase, "UncompressedTestFiles"); + var testFiles = new Dictionary(); + foreach (var file in Directory.EnumerateFiles(assetsFolder, null, SearchOption.AllDirectories)) { - 0 => CompressionLevel.NoCompression, - 1 => CompressionLevel.Fastest, - 6 => CompressionLevel.Optimal, - 9 => CompressionLevel.SmallestSize - }; + var key = Path.GetRelativePath(assetsFolder, file); + testFiles.Add(key, File.ReadAllBytes(file)); + } + + TestFiles = testFiles; + } + + public static Dictionary TestFiles { get; set; } + public static IEnumerable GetInputs() => from key in TestFiles.Keys + from level in Levels + select new object[] { key, level }; + +#pragma warning disable CS8509 // The switch expression does not handle all possible values of its input type (it is not exhaustive). + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static CompressionLevel ToLevelEnum(int level) => level switch + { + 0 => CompressionLevel.NoCompression, + 1 => CompressionLevel.Fastest, + -1 => CompressionLevel.Optimal, + 9 => CompressionLevel.SmallestSize + }; +#pragma warning restore CS8509 // The switch expression does not handle all possible values of its input type (it is not exhaustive). + + [Benchmark(Baseline = true)] + [ArgumentsSource(nameof(GetInputs))] + public void DeflateSIO(string testFile, int level) + { var input = TestFiles[testFile]; using var outputMs = new MemoryStream(input.Length); - using var deflateStream = new DeflateStream(outputMs, compressionLevel); + using var deflateStream = new DeflateStream(outputMs, ToLevelEnum(level)); deflateStream.Write(input); } [Benchmark] - [ArgumentsSource(nameof(Inputs))] - public void DeflateLibdeflate(string testFile, int level) + [ArgumentsSource(nameof(GetInputs))] + public void DeflateLibdeflate_MemoryOwner(string testFile, int level) { var input = TestFiles[testFile]; using var compressor = new DeflateCompressor(level); using var owner = compressor.Compress(input); } + + [Benchmark] + [ArgumentsSource(nameof(GetInputs))] + public void DeflateLibdeflate_Buffer(string testFile, int level) + { + var input = TestFiles[testFile]; + using var compressor = new DeflateCompressor(level); + var output = new byte[input.Length]; + var bytesWritten = compressor.Compress(input, output); + } } From 727f08e913a91c074e3cb7135df2e2a58708e331 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sat, 27 Jan 2024 21:36:47 -0600 Subject: [PATCH 18/23] Use unsafe options --- src/LibDeflate/Imports/Compression.cs | 5 +- src/LibDeflate/Imports/Constants.cs | 2 - .../Imports/CustomMemoryAllocator.cs | 4 +- src/LibDeflate/Imports/Decompression.cs | 5 +- src/LibDeflate/Imports/libdeflate_options.cs | 48 +++---------------- .../CustomMemoryAllocatorTests.cs | 22 +++++++-- 6 files changed, 33 insertions(+), 53 deletions(-) diff --git a/src/LibDeflate/Imports/Compression.cs b/src/LibDeflate/Imports/Compression.cs index aad4902..e63e761 100644 --- a/src/LibDeflate/Imports/Compression.cs +++ b/src/LibDeflate/Imports/Compression.cs @@ -33,8 +33,9 @@ internal static partial class Compression /// /// Like but allows specifying advanced options per-compressor. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern libdeflate_compressor libdeflate_alloc_compressor_ex(int compression_level, in libdeflate_options options); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_compressor libdeflate_alloc_compressor_ex(int compression_level, in libdeflate_options options); /// /// libdeflate_deflate_compress() performs raw DEFLATE compression on a buffer of diff --git a/src/LibDeflate/Imports/Constants.cs b/src/LibDeflate/Imports/Constants.cs index 9cfcc19..a1df3e4 100644 --- a/src/LibDeflate/Imports/Constants.cs +++ b/src/LibDeflate/Imports/Constants.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; [assembly: InternalsVisibleTo($"{nameof(LibDeflate)}.Tests")] [assembly: InternalsVisibleTo($"{nameof(LibDeflate)}.DangerousTests")] @@ -9,5 +8,4 @@ namespace LibDeflate.Imports; internal static class Constants { public const string DllName = "libdeflate"; - public const CallingConvention CallConv = CallingConvention.Cdecl; } diff --git a/src/LibDeflate/Imports/CustomMemoryAllocator.cs b/src/LibDeflate/Imports/CustomMemoryAllocator.cs index 992e96f..07f76e1 100644 --- a/src/LibDeflate/Imports/CustomMemoryAllocator.cs +++ b/src/LibDeflate/Imports/CustomMemoryAllocator.cs @@ -4,14 +4,14 @@ namespace LibDeflate.Imports; -using size_t = UIntPtr; +using size_t = nuint; internal static partial class CustomMemoryAllocator { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate IntPtr malloc_func(size_t size); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void free_func(IntPtr alloc); + public delegate void free_func(nint alloc); /// /// Install a custom memory allocator which libdeflate will use for all memory diff --git a/src/LibDeflate/Imports/Decompression.cs b/src/LibDeflate/Imports/Decompression.cs index 9b08150..476997a 100644 --- a/src/LibDeflate/Imports/Decompression.cs +++ b/src/LibDeflate/Imports/Decompression.cs @@ -56,8 +56,9 @@ public enum libdeflate_result /// /// Like but allows specifying advanced options per-decompressor. /// - [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] - public static extern libdeflate_decompressor libdeflate_alloc_decompressor_ex(in libdeflate_options options); + [LibraryImport(Constants.DllName)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial libdeflate_decompressor libdeflate_alloc_decompressor_ex(in libdeflate_options options); /// /// libdeflate_deflate_decompress() decompresses the DEFLATE-compressed stream diff --git a/src/LibDeflate/Imports/libdeflate_options.cs b/src/LibDeflate/Imports/libdeflate_options.cs index 8367689..52c8dd9 100644 --- a/src/LibDeflate/Imports/libdeflate_options.cs +++ b/src/LibDeflate/Imports/libdeflate_options.cs @@ -1,10 +1,6 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -namespace LibDeflate.Imports; +namespace LibDeflate.Imports; -using static LibDeflate.Imports.CustomMemoryAllocator; -using size_t = UIntPtr; +using size_t = nuint; /// /// Advanced options. This is the options structure that @@ -13,44 +9,14 @@ namespace LibDeflate.Imports; /// require. Most users won't need this and should just use the non-"_ex" /// functions instead. /// -internal readonly struct libdeflate_options +internal readonly unsafe struct libdeflate_options(delegate* unmanaged[Cdecl] malloc, delegate* unmanaged[Cdecl] free)//(size_t sizeof_options, malloc_func malloc, free_func free) { - private static readonly size_t Size = (nuint)(nint)Unsafe.SizeOf(); - - public libdeflate_options(malloc_func malloc, free_func free) - { -#if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(malloc); - ArgumentNullException.ThrowIfNull(free); -#else - ThrowIfNull(malloc); - ThrowIfNull(free); -#endif - - this.sizeof_options = Size; - this.malloc = malloc; - this.free = free; - -#if !NET6_0_OR_GREATER - static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null) - { - if(argument is null) - { - ThrowHelperArgumentNull(paramName!); - } - - [DoesNotReturn] - static void ThrowHelperArgumentNull(string paramName) => throw new ArgumentNullException(paramName); - } -#endif - } - /// /// This field must be set to the struct size. This field exists for /// extensibility, so that fields can be appended to this struct in /// future versions of libdeflate while still supporting old binaries. /// - public readonly size_t sizeof_options; + public readonly size_t sizeof_options = (size_t)sizeof(libdeflate_options); /// /// An optional custom memory allocator to use for this (de)compressor. @@ -68,7 +34,7 @@ static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression(na /// call the "global" memory allocator if a per-(de)compressor custom /// allocator is always given. /// - public readonly malloc_func malloc; + public readonly delegate* unmanaged[Cdecl] malloc = malloc; /// /// An optional custom memory deallocator to use for this (de)compressor. @@ -86,5 +52,5 @@ static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression(na /// call the "global" memory allocator if a per-(de)compressor custom /// allocator is always given. /// - public readonly free_func free; -} + public readonly delegate* unmanaged[Cdecl] free = free; +} \ No newline at end of file diff --git a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs index 4faf3e6..136e357 100644 --- a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs +++ b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs @@ -1,4 +1,5 @@ using LibDeflate.Imports; +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Xunit; @@ -60,22 +61,32 @@ public void UseGlobalCustomAllocatorsTest() } [Fact] - public void UsePerCompressorCustomAllocatorsTest() + public unsafe void UsePerCompressorCustomAllocatorsTest() { int startingGlobalMallocs = mallocCount; int startingGlobalFrees = freeCount; int localMallocs = 0; int localFrees = 0; - var options = new libdeflate_options((nuint len) => + + CustomMemoryAllocator.malloc_func malloc; + malloc = (nuint len) => { localMallocs++; return Marshal.AllocHGlobal((nint)len); - }, (nint alloc) => + }; + + CustomMemoryAllocator.free_func free; + free = (nint alloc) => { localFrees++; Marshal.FreeHGlobal(alloc); - }); + }; + + var options = new libdeflate_options( + (delegate* unmanaged[Cdecl])Marshal.GetFunctionPointerForDelegate(malloc), + (delegate* unmanaged[Cdecl])Marshal.GetFunctionPointerForDelegate(free) + ); //test compressor { @@ -98,6 +109,9 @@ public void UsePerCompressorCustomAllocatorsTest() Assert.Equal(2, localFrees); Assert.Equal(startingGlobalFrees, freeCount); } + + GC.KeepAlive(malloc); + GC.KeepAlive(free); } [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] From 38b683c9a38375c56b2b46be59d25dba4ef17346 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sat, 27 Jan 2024 21:45:35 -0600 Subject: [PATCH 19/23] Use compact collection expression --- src/LibDeflate/Imports/Checksums.cs | 4 ++-- src/LibDeflate/Imports/Compression.cs | 18 +++++++++--------- .../Imports/CustomMemoryAllocator.cs | 4 ++-- src/LibDeflate/Imports/Decompression.cs | 18 +++++++++--------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/LibDeflate/Imports/Checksums.cs b/src/LibDeflate/Imports/Checksums.cs index 2659e63..6ff6837 100644 --- a/src/LibDeflate/Imports/Checksums.cs +++ b/src/LibDeflate/Imports/Checksums.cs @@ -15,7 +15,7 @@ internal static partial class Checksums /// 'buffer' is specified as NULL. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial UInt32 libdeflate_adler32(UInt32 adler, in byte buffer, size_t len); /// @@ -25,6 +25,6 @@ internal static partial class Checksums /// specified as NULL. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial UInt32 libdeflate_crc32(UInt32 crc, in byte buffer, size_t len); } diff --git a/src/LibDeflate/Imports/Compression.cs b/src/LibDeflate/Imports/Compression.cs index e63e761..ed9baff 100644 --- a/src/LibDeflate/Imports/Compression.cs +++ b/src/LibDeflate/Imports/Compression.cs @@ -27,14 +27,14 @@ internal static partial class Compression /// However, different threads may use different compressors concurrently. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial libdeflate_compressor libdeflate_alloc_compressor(int compression_level); /// /// Like but allows specifying advanced options per-compressor. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial libdeflate_compressor libdeflate_alloc_compressor_ex(int compression_level, in libdeflate_options options); /// @@ -45,7 +45,7 @@ internal static partial class Compression /// could not be compressed to 'out_nbytes_avail' bytes or fewer. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial size_t libdeflate_deflate_compress(libdeflate_compressor compressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail); /// @@ -74,7 +74,7 @@ internal static partial class Compression /// did not fit into the provided output buffer. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial size_t libdeflate_deflate_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); /// @@ -82,7 +82,7 @@ internal static partial class Compression /// format. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial size_t libdeflate_zlib_compress(libdeflate_compressor compressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail); /// @@ -91,7 +91,7 @@ internal static partial class Compression /// libdeflate_deflate_compress(). /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial size_t libdeflate_zlib_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); /// @@ -99,7 +99,7 @@ internal static partial class Compression /// format. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial size_t libdeflate_gzip_compress(libdeflate_compressor compressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail); /// @@ -108,7 +108,7 @@ internal static partial class Compression /// libdeflate_deflate_compress(). /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial size_t libdeflate_gzip_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); /// @@ -117,6 +117,6 @@ internal static partial class Compression /// taken. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial void libdeflate_free_compressor(libdeflate_compressor compressor); } diff --git a/src/LibDeflate/Imports/CustomMemoryAllocator.cs b/src/LibDeflate/Imports/CustomMemoryAllocator.cs index 07f76e1..b6825ba 100644 --- a/src/LibDeflate/Imports/CustomMemoryAllocator.cs +++ b/src/LibDeflate/Imports/CustomMemoryAllocator.cs @@ -22,7 +22,7 @@ internal static partial class CustomMemoryAllocator /// structures in existence when calling this function. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial void libdeflate_set_memory_allocator(malloc_func malloc, free_func free); /// @@ -34,6 +34,6 @@ internal static partial class CustomMemoryAllocator /// structures in existence when calling this function. /// [LibraryImport(Constants.DllName, EntryPoint = "libdeflate_set_memory_allocator")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static unsafe partial void libdeflate_set_memory_allocator_unsafe(delegate* unmanaged[Cdecl] malloc, delegate* unmanaged[Cdecl] free); } diff --git a/src/LibDeflate/Imports/Decompression.cs b/src/LibDeflate/Imports/Decompression.cs index 476997a..58c6531 100644 --- a/src/LibDeflate/Imports/Decompression.cs +++ b/src/LibDeflate/Imports/Decompression.cs @@ -50,14 +50,14 @@ public enum libdeflate_result /// However, different threads may use different decompressors concurrently. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial libdeflate_decompressor libdeflate_alloc_decompressor(); /// /// Like but allows specifying advanced options per-decompressor. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial libdeflate_decompressor libdeflate_alloc_decompressor_ex(in libdeflate_options options); /// @@ -93,7 +93,7 @@ public enum libdeflate_result /// nonzero result code if decompression failed for another reason. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial libdeflate_result libdeflate_deflate_decompress(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_out_nbytes_ret); /// @@ -103,7 +103,7 @@ public enum libdeflate_result /// byte boundary) is written to *actual_in_nbytes_ret. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial libdeflate_result libdeflate_deflate_decompress_ex(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_in_nbytes_ret, out size_t actual_out_nbytes_ret); /// @@ -115,7 +115,7 @@ public enum libdeflate_result /// use libdeflate_zlib_decompress_ex(). /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial libdeflate_result libdeflate_zlib_decompress(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_out_nbytes_ret); /// @@ -127,7 +127,7 @@ public enum libdeflate_result /// use libdeflate_zlib_decompress_ex(). /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial libdeflate_result libdeflate_zlib_decompress_ex(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_in_nbytes_ret, out size_t actual_out_nbytes_ret); /// @@ -139,7 +139,7 @@ public enum libdeflate_result /// multi-member support. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial libdeflate_result libdeflate_gzip_decompress(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_out_nbytes_ret); /// @@ -150,7 +150,7 @@ public enum libdeflate_result /// written to *actual_in_nbytes_ret. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial libdeflate_result libdeflate_gzip_decompress_ex(libdeflate_decompressor decompressor, in byte @in, size_t in_nbytes, ref byte @out, size_t out_nbytes_avail, out size_t actual_in_nbytes_ret, out size_t actual_out_nbytes_ret); /// @@ -159,6 +159,6 @@ public enum libdeflate_result /// is taken. /// [LibraryImport(Constants.DllName)] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial void libdeflate_free_decompressor(libdeflate_decompressor compressor); } \ No newline at end of file From 18811944cf6e703208df1b226406c26112fef0c8 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 28 Jan 2024 12:18:50 -0600 Subject: [PATCH 20/23] Use collection expressions --- test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs index 136e357..b71f8bc 100644 --- a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs +++ b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs @@ -114,14 +114,14 @@ public unsafe void UsePerCompressorCustomAllocatorsTest() GC.KeepAlive(free); } - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] private static unsafe void* malloc_unsafe(nuint len) { mallocCount++; return NativeMemory.Alloc(len); } - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] private static unsafe void free_unsafe(void* alloc) { freeCount++; From 56cc1374a08c4475f214c837023d3176f4d5ee32 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 28 Jan 2024 12:19:36 -0600 Subject: [PATCH 21/23] Simplify checksum impls and add readonly modifiers --- src/LibDeflate/Checksums/Adler32.cs | 21 +++++---------------- src/LibDeflate/Checksums/Crc32.cs | 2 +- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/LibDeflate/Checksums/Adler32.cs b/src/LibDeflate/Checksums/Adler32.cs index f19661c..f03f260 100644 --- a/src/LibDeflate/Checksums/Adler32.cs +++ b/src/LibDeflate/Checksums/Adler32.cs @@ -7,25 +7,14 @@ namespace LibDeflate.Checksums; public struct Adler32 { - private bool _initialized; - //because we have to supply 1 as the initial value - //we must always retrieve this through Hash private uint _currentAdler; - public uint Hash - { - get - { - if (!_initialized) - { - _currentAdler = 1; - _initialized = true; - } - return _currentAdler; - } - } + + public Adler32() => _currentAdler = 1; + + public readonly uint Hash => _currentAdler; public void Append(ReadOnlySpan input) - => _currentAdler = AppendCore(Hash, input); + => _currentAdler = AppendCore(_currentAdler, input); public uint Compute(ReadOnlySpan input) => _currentAdler = AppendCore(1, input); diff --git a/src/LibDeflate/Checksums/Crc32.cs b/src/LibDeflate/Checksums/Crc32.cs index 1ca2da3..41e0b0b 100644 --- a/src/LibDeflate/Checksums/Crc32.cs +++ b/src/LibDeflate/Checksums/Crc32.cs @@ -9,7 +9,7 @@ public struct Crc32 { private uint _currentCrc; - public uint Hash => _currentCrc; + public readonly uint Hash => _currentCrc; public void Append(ReadOnlySpan input) => _currentCrc = AppendCore(_currentCrc, input); From c357dcebbe7a8ec8c5eead0de7018e5187034b5e Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 28 Jan 2024 12:19:53 -0600 Subject: [PATCH 22/23] Simplify type names --- src/LibDeflate/Imports/Checksums.cs | 7 +++---- src/LibDeflate/Imports/Decompression.cs | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/LibDeflate/Imports/Checksums.cs b/src/LibDeflate/Imports/Checksums.cs index 6ff6837..4117d71 100644 --- a/src/LibDeflate/Imports/Checksums.cs +++ b/src/LibDeflate/Imports/Checksums.cs @@ -1,5 +1,4 @@ -using System; -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LibDeflate.Imports; @@ -16,7 +15,7 @@ internal static partial class Checksums /// [LibraryImport(Constants.DllName)] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - public static partial UInt32 libdeflate_adler32(UInt32 adler, in byte buffer, size_t len); + public static partial uint libdeflate_adler32(uint adler, in byte buffer, size_t len); /// /// libdeflate_crc32() updates a running CRC-32 checksum with 'len' bytes of data @@ -26,5 +25,5 @@ internal static partial class Checksums /// [LibraryImport(Constants.DllName)] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - public static partial UInt32 libdeflate_crc32(UInt32 crc, in byte buffer, size_t len); + public static partial uint libdeflate_crc32(uint crc, in byte buffer, size_t len); } diff --git a/src/LibDeflate/Imports/Decompression.cs b/src/LibDeflate/Imports/Decompression.cs index 58c6531..cfd1551 100644 --- a/src/LibDeflate/Imports/Decompression.cs +++ b/src/LibDeflate/Imports/Decompression.cs @@ -3,8 +3,8 @@ namespace LibDeflate.Imports; -using libdeflate_decompressor = System.IntPtr; -using size_t = System.UIntPtr; +using libdeflate_decompressor = nint; +using size_t = nuint; internal static partial class Decompression { From 16558da7905e2950cf0bb2c1179a5e8836254794 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 28 Jan 2024 12:19:59 -0600 Subject: [PATCH 23/23] Disable runtime marshalling --- src/LibDeflate/Imports/Constants.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/LibDeflate/Imports/Constants.cs b/src/LibDeflate/Imports/Constants.cs index a1df3e4..4dfe053 100644 --- a/src/LibDeflate/Imports/Constants.cs +++ b/src/LibDeflate/Imports/Constants.cs @@ -3,6 +3,8 @@ [assembly: InternalsVisibleTo($"{nameof(LibDeflate)}.Tests")] [assembly: InternalsVisibleTo($"{nameof(LibDeflate)}.DangerousTests")] [assembly: InternalsVisibleTo($"{nameof(LibDeflate)}.Benchmarks")] +[assembly: DisableRuntimeMarshalling] + namespace LibDeflate.Imports; internal static class Constants