From 5865e66c413948c637b4100a4d74c75b2b4e2847 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 19:30:45 -0500 Subject: [PATCH 01/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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 514df5a3c8395517d4e39943677298b734441d3b Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 17 Sep 2023 15:13:47 -0500 Subject: [PATCH 09/44] Bump test package versions --- .../LibDeflate.DangerousTests.csproj | 8 ++++---- test/LibDeflate.Tests/LibDeflate.Tests.csproj | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj b/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj index 099dc8f..c0837af 100644 --- a/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj +++ b/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj @@ -17,13 +17,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/LibDeflate.Tests/LibDeflate.Tests.csproj b/test/LibDeflate.Tests/LibDeflate.Tests.csproj index 5adb742..5c6813f 100644 --- a/test/LibDeflate.Tests/LibDeflate.Tests.csproj +++ b/test/LibDeflate.Tests/LibDeflate.Tests.csproj @@ -8,13 +8,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 6f97def5f78b6698b56aa1559271c736c64e61c4 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 17 Sep 2023 15:14:13 -0500 Subject: [PATCH 10/44] Bump benchmark package versions --- bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj b/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj index aa69a7d..23728b0 100644 --- a/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj +++ b/bench/LibDeflate.Benchmarks/LibDeflate.Benchmarks.csproj @@ -7,7 +7,7 @@ - + From 72528f0c5bb1a261a35b3d81fc9048b670606a3f Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 17 Sep 2023 15:17:22 -0500 Subject: [PATCH 11/44] Bump polysharp version --- src/LibDeflate/LibDeflate.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LibDeflate/LibDeflate.csproj b/src/LibDeflate/LibDeflate.csproj index 4c51334..ab20995 100644 --- a/src/LibDeflate/LibDeflate.csproj +++ b/src/LibDeflate/LibDeflate.csproj @@ -33,7 +33,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From ebcd829369a8830638def4446e9546049af5c6ac Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Mon, 18 Sep 2023 15:42:48 -0500 Subject: [PATCH 12/44] Add libdeflate_options --- .../Imports/CustomMemoryAllocator.cs | 70 ++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/src/LibDeflate/Imports/CustomMemoryAllocator.cs b/src/LibDeflate/Imports/CustomMemoryAllocator.cs index 7549b85..8e9b2d8 100644 --- a/src/LibDeflate/Imports/CustomMemoryAllocator.cs +++ b/src/LibDeflate/Imports/CustomMemoryAllocator.cs @@ -1,6 +1,6 @@ using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; - namespace LibDeflate.Imports; using size_t = UIntPtr; @@ -22,4 +22,72 @@ internal static class CustomMemoryAllocator /// [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] public static extern void libdeflate_set_memory_allocator(malloc_func malloc, free_func free); + + internal readonly struct libdeflate_options + { + 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 + //TODO: add throwhelpers + if(malloc is null) + { + throw new ArgumentNullException(nameof(malloc)); + } + + if(free is null) + { + throw new ArgumentNullException(nameof(free)); + } +#endif + + this.malloc = malloc; + this.free = free; + } + + /// + /// 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 = Size; + /// + /// An optional custom memory allocator to use for this (de)compressor. + /// 'malloc_func' must be a function that behaves like malloc(). + /// + /// + /// This is useful in cases where a process might have multiple users of + /// libdeflate who want to use different memory allocators. For example, + /// a library might want to use libdeflate with a custom memory allocator + /// without interfering with user code that might use libdeflate too. + /// + /// This takes priority over the "global" memory allocator (which by + /// default is malloc() and free(), but can be changed by + /// libdeflate_set_memory_allocator()). Moreover, libdeflate will never + /// call the "global" memory allocator if a per-(de)compressor custom + /// allocator is always given. + /// + public readonly malloc_func malloc; + /// + /// An optional custom memory deallocator to use for this (de)compressor. + /// 'free_func' must be a function that behaves like free(). + /// + /// + /// This is useful in cases where a process might have multiple users of + /// libdeflate who want to use different memory allocators. For example, + /// a library might want to use libdeflate with a custom memory allocator + /// without interfering with user code that might use libdeflate too. + /// + /// This takes priority over the "global" memory allocator (which by + /// default is malloc() and free(), but can be changed by + /// libdeflate_set_memory_allocator()). Moreover, libdeflate will never + /// call the "global" memory allocator if a per-(de)compressor custom + /// allocator is always given. + /// + public readonly free_func free; + } } From fe8bda69206cd493aeebbdfa476207c4d832583a Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Mon, 18 Sep 2023 15:43:12 -0500 Subject: [PATCH 13/44] Add CustomMemoryAllocatorTests --- .../CustomMemoryAllocatorTests.cs | 67 +++++++++++++++++-- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs index 655810f..507dd10 100644 --- a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs +++ b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs @@ -24,16 +24,69 @@ private static void free(nint alloc) } [Fact] - public void UseCustomAllocatorsTest() + public void UseGlobalCustomAllocatorsTest() { CustomMemoryAllocator.libdeflate_set_memory_allocator(malloc, free); - //allocate something - var compressor = Compression.libdeflate_alloc_compressor(0); - Assert.Equal(1, mallocCount); + //test compressor + { + //allocate something + var compressor = Compression.libdeflate_alloc_compressor(0); + Assert.Equal(1, mallocCount); - //free something - Compression.libdeflate_free_compressor(compressor); - Assert.Equal(1, freeCount); + //free something + Compression.libdeflate_free_compressor(compressor); + Assert.Equal(1, freeCount); + } + + //test decompressor + { + var decompressor = Decompression.libdeflate_alloc_decompressor(); + Assert.Equal(2, mallocCount); + + Decompression.libdeflate_free_decompressor(decompressor); + Assert.Equal(2, freeCount); + } + } + + [Fact] + public void UsePerCompressorCustomAllocatorsTest() + { + int startingGlobalMallocs = mallocCount; + int startingGlobalFrees = freeCount; + + int localMallocs = 0; + int localFrees = 0; + var options = new CustomMemoryAllocator.libdeflate_options((nuint len) => + { + localMallocs++; + return Marshal.AllocHGlobal((nint)len); + }, (nint alloc) => + { + localFrees++; + Marshal.FreeHGlobal(alloc); + }); + + //test compressor + { + var compressor = Compression.libdeflate_alloc_compressor_ex(0, options); + Assert.Equal(1, localMallocs); + Assert.Equal(startingGlobalMallocs, mallocCount); + + Compression.libdeflate_free_compressor(compressor); + Assert.Equal(1, localFrees); + Assert.Equal(startingGlobalFrees, freeCount); + } + + //test decompressor + { + var decompressor = Decompression.libdeflate_alloc_decompressor_ex(options); + Assert.Equal(2, localMallocs); + Assert.Equal(startingGlobalMallocs, mallocCount); + + Decompression.libdeflate_free_decompressor(decompressor); + Assert.Equal(2, localFrees); + Assert.Equal(startingGlobalFrees, freeCount); + } } } From 3caba62cad30396108f30c6cf9a6cb2e3b750c1f Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Mon, 18 Sep 2023 15:49:16 -0500 Subject: [PATCH 14/44] Add more documentation to libdeflate_options --- src/LibDeflate/Imports/CustomMemoryAllocator.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/LibDeflate/Imports/CustomMemoryAllocator.cs b/src/LibDeflate/Imports/CustomMemoryAllocator.cs index 8e9b2d8..904ec25 100644 --- a/src/LibDeflate/Imports/CustomMemoryAllocator.cs +++ b/src/LibDeflate/Imports/CustomMemoryAllocator.cs @@ -23,6 +23,13 @@ internal static class CustomMemoryAllocator [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] public static extern void libdeflate_set_memory_allocator(malloc_func malloc, free_func free); + /// + /// Advanced options. This is the options structure that + /// + /// and + /// require. Most users won't need this and should just use the non-"_ex" + /// functions instead. + /// internal readonly struct libdeflate_options { private static readonly size_t Size = (nuint)(nint)Unsafe.SizeOf(); From 84744d23fcf8aa875ac7551070ff598b01cb45e0 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Mon, 18 Sep 2023 15:49:29 -0500 Subject: [PATCH 15/44] Add libdeflate_alloc_compressor_ex --- src/LibDeflate/Imports/Compression.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/LibDeflate/Imports/Compression.cs b/src/LibDeflate/Imports/Compression.cs index 094b143..f178d98 100644 --- a/src/LibDeflate/Imports/Compression.cs +++ b/src/LibDeflate/Imports/Compression.cs @@ -28,6 +28,12 @@ internal static class Compression [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] public static extern libdeflate_compressor libdeflate_alloc_compressor(int compression_level); + /// + /// Like but allows specifying advanced options per-compressor. + /// + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern libdeflate_compressor libdeflate_alloc_compressor_ex(int compression_level, in CustomMemoryAllocator.libdeflate_options options); + /// /// libdeflate_deflate_compress() performs raw DEFLATE compression on a buffer of /// data. The function attempts to compress 'in_nbytes' bytes of data located at @@ -38,7 +44,6 @@ internal static class Compression [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); - /// /// libdeflate_deflate_compress_bound() returns a worst-case upper bound on the /// number of bytes of compressed data that may be produced by compressing any From 4414230fefdef76b6acd87e337321095a7f5ae26 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Mon, 18 Sep 2023 15:49:49 -0500 Subject: [PATCH 16/44] Add libdeflate_alloc_decompressor_ex --- src/LibDeflate/Imports/Decompression.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/LibDeflate/Imports/Decompression.cs b/src/LibDeflate/Imports/Decompression.cs index f32a4e3..45bd969 100644 --- a/src/LibDeflate/Imports/Decompression.cs +++ b/src/LibDeflate/Imports/Decompression.cs @@ -51,6 +51,12 @@ public enum libdeflate_result [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] public static extern libdeflate_decompressor libdeflate_alloc_decompressor(); + /// + /// Like but allows specifying advanced options per-decompressor. + /// + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern libdeflate_decompressor libdeflate_alloc_decompressor_ex(in CustomMemoryAllocator.libdeflate_options options); + /// /// libdeflate_deflate_decompress() decompresses the DEFLATE-compressed stream /// from the buffer 'in' with compressed size up to 'in_nbytes' bytes. The @@ -144,5 +150,5 @@ public enum libdeflate_result /// is taken. /// [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern void libdeflate_free_decompressor(libdeflate_decompressor compressor); + public static extern void libdeflate_free_decompressor(libdeflate_decompressor decompressor); } \ No newline at end of file From 2091152478eba0c7fe1308c509a2560f81bf4564 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Mon, 18 Sep 2023 15:50:26 -0500 Subject: [PATCH 17/44] Bump package version and native package version to v1.19.0 --- src/LibDeflate/LibDeflate.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LibDeflate/LibDeflate.csproj b/src/LibDeflate/LibDeflate.csproj index ab20995..469f04c 100644 --- a/src/LibDeflate/LibDeflate.csproj +++ b/src/LibDeflate/LibDeflate.csproj @@ -3,7 +3,7 @@ net6.0;netstandard2.0 LibDeflate.NET - 1.18.0 + 1.19.0 jzebedee MIT https://github.com/jzebedee/LibDeflate @@ -45,7 +45,7 @@ - + From 309a6902bf9121f85cfcd85504e90bbabbcc58bf Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Mon, 18 Sep 2023 16:11:20 -0500 Subject: [PATCH 18/44] Break out libdeflate_options from CustomMemoryAllocator --- src/LibDeflate/Imports/Compression.cs | 2 +- .../Imports/CustomMemoryAllocator.cs | 79 +----------------- src/LibDeflate/Imports/Decompression.cs | 2 +- src/LibDeflate/Imports/libdeflate_options.cs | 83 +++++++++++++++++++ .../CustomMemoryAllocatorTests.cs | 2 +- 5 files changed, 88 insertions(+), 80 deletions(-) create mode 100644 src/LibDeflate/Imports/libdeflate_options.cs diff --git a/src/LibDeflate/Imports/Compression.cs b/src/LibDeflate/Imports/Compression.cs index f178d98..de20e18 100644 --- a/src/LibDeflate/Imports/Compression.cs +++ b/src/LibDeflate/Imports/Compression.cs @@ -32,7 +32,7 @@ internal static class Compression /// Like but allows specifying advanced options per-compressor. /// [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern libdeflate_compressor libdeflate_alloc_compressor_ex(int compression_level, in CustomMemoryAllocator.libdeflate_options options); + public static extern 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/CustomMemoryAllocator.cs b/src/LibDeflate/Imports/CustomMemoryAllocator.cs index 904ec25..3bf1839 100644 --- a/src/LibDeflate/Imports/CustomMemoryAllocator.cs +++ b/src/LibDeflate/Imports/CustomMemoryAllocator.cs @@ -1,6 +1,6 @@ using System; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace LibDeflate.Imports; using size_t = UIntPtr; @@ -22,79 +22,4 @@ internal static class CustomMemoryAllocator /// [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] public static extern void libdeflate_set_memory_allocator(malloc_func malloc, free_func free); - - /// - /// Advanced options. This is the options structure that - /// - /// and - /// require. Most users won't need this and should just use the non-"_ex" - /// functions instead. - /// - internal readonly struct libdeflate_options - { - 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 - //TODO: add throwhelpers - if(malloc is null) - { - throw new ArgumentNullException(nameof(malloc)); - } - - if(free is null) - { - throw new ArgumentNullException(nameof(free)); - } -#endif - - this.malloc = malloc; - this.free = free; - } - - /// - /// 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 = Size; - /// - /// An optional custom memory allocator to use for this (de)compressor. - /// 'malloc_func' must be a function that behaves like malloc(). - /// - /// - /// This is useful in cases where a process might have multiple users of - /// libdeflate who want to use different memory allocators. For example, - /// a library might want to use libdeflate with a custom memory allocator - /// without interfering with user code that might use libdeflate too. - /// - /// This takes priority over the "global" memory allocator (which by - /// default is malloc() and free(), but can be changed by - /// libdeflate_set_memory_allocator()). Moreover, libdeflate will never - /// call the "global" memory allocator if a per-(de)compressor custom - /// allocator is always given. - /// - public readonly malloc_func malloc; - /// - /// An optional custom memory deallocator to use for this (de)compressor. - /// 'free_func' must be a function that behaves like free(). - /// - /// - /// This is useful in cases where a process might have multiple users of - /// libdeflate who want to use different memory allocators. For example, - /// a library might want to use libdeflate with a custom memory allocator - /// without interfering with user code that might use libdeflate too. - /// - /// This takes priority over the "global" memory allocator (which by - /// default is malloc() and free(), but can be changed by - /// libdeflate_set_memory_allocator()). Moreover, libdeflate will never - /// call the "global" memory allocator if a per-(de)compressor custom - /// allocator is always given. - /// - public readonly free_func free; - } -} +} \ No newline at end of file diff --git a/src/LibDeflate/Imports/Decompression.cs b/src/LibDeflate/Imports/Decompression.cs index 45bd969..8c343f2 100644 --- a/src/LibDeflate/Imports/Decompression.cs +++ b/src/LibDeflate/Imports/Decompression.cs @@ -55,7 +55,7 @@ public enum libdeflate_result /// Like but allows specifying advanced options per-decompressor. /// [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern libdeflate_decompressor libdeflate_alloc_decompressor_ex(in CustomMemoryAllocator.libdeflate_options options); + public static extern 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 new file mode 100644 index 0000000..4fd7a43 --- /dev/null +++ b/src/LibDeflate/Imports/libdeflate_options.cs @@ -0,0 +1,83 @@ +using System; +using System.Runtime.CompilerServices; +namespace LibDeflate.Imports; + +using static LibDeflate.Imports.CustomMemoryAllocator; +using size_t = UIntPtr; + +/// +/// Advanced options. This is the options structure that +/// +/// and +/// require. Most users won't need this and should just use the non-"_ex" +/// functions instead. +/// +internal readonly struct libdeflate_options +{ + 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 + //TODO: add throwhelpers + if (malloc is null) + { + throw new ArgumentNullException(nameof(malloc)); + } + + if (free is null) + { + throw new ArgumentNullException(nameof(free)); + } +#endif + this.sizeof_options = Size; + this.malloc = malloc; + this.free = free; + } + + /// + /// 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; + + /// + /// An optional custom memory allocator to use for this (de)compressor. + /// 'malloc_func' must be a function that behaves like malloc(). + /// + /// + /// This is useful in cases where a process might have multiple users of + /// libdeflate who want to use different memory allocators. For example, + /// a library might want to use libdeflate with a custom memory allocator + /// without interfering with user code that might use libdeflate too. + /// + /// This takes priority over the "global" memory allocator (which by + /// default is malloc() and free(), but can be changed by + /// libdeflate_set_memory_allocator()). Moreover, libdeflate will never + /// call the "global" memory allocator if a per-(de)compressor custom + /// allocator is always given. + /// + public readonly malloc_func malloc; + + /// + /// An optional custom memory deallocator to use for this (de)compressor. + /// 'free_func' must be a function that behaves like free(). + /// + /// + /// This is useful in cases where a process might have multiple users of + /// libdeflate who want to use different memory allocators. For example, + /// a library might want to use libdeflate with a custom memory allocator + /// without interfering with user code that might use libdeflate too. + /// + /// This takes priority over the "global" memory allocator (which by + /// default is malloc() and free(), but can be changed by + /// libdeflate_set_memory_allocator()). Moreover, libdeflate will never + /// call the "global" memory allocator if a per-(de)compressor custom + /// allocator is always given. + /// + public readonly free_func free; +} diff --git a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs index 507dd10..2f2f2d8 100644 --- a/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs +++ b/test/LibDeflate.DangerousTests/CustomMemoryAllocatorTests.cs @@ -57,7 +57,7 @@ public void UsePerCompressorCustomAllocatorsTest() int localMallocs = 0; int localFrees = 0; - var options = new CustomMemoryAllocator.libdeflate_options((nuint len) => + var options = new libdeflate_options((nuint len) => { localMallocs++; return Marshal.AllocHGlobal((nint)len); From cc63207b2fcb0c14e3ca8ffb87cb8389152c8e05 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Mon, 18 Sep 2023 21:53:27 -0500 Subject: [PATCH 19/44] Only enable trimming for TFMs that support it --- src/LibDeflate/LibDeflate.csproj | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/LibDeflate/LibDeflate.csproj b/src/LibDeflate/LibDeflate.csproj index 469f04c..6569ea9 100644 --- a/src/LibDeflate/LibDeflate.csproj +++ b/src/LibDeflate/LibDeflate.csproj @@ -12,7 +12,6 @@ latest enable - true true @@ -21,6 +20,12 @@ snupkg + + + + true + + true From dbc01674b03f74b22f810b4e867085e78698bd86 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Mon, 18 Sep 2023 21:53:59 -0500 Subject: [PATCH 20/44] Fix expected and actual being reversed --- test/LibDeflate.Tests/ImportTests/CompressionTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/LibDeflate.Tests/ImportTests/CompressionTests.cs b/test/LibDeflate.Tests/ImportTests/CompressionTests.cs index 83c2875..32573c7 100644 --- a/test/LibDeflate.Tests/ImportTests/CompressionTests.cs +++ b/test/LibDeflate.Tests/ImportTests/CompressionTests.cs @@ -21,7 +21,7 @@ public void AllocAndFreeCompressorTest(int compressionLevel) var compressor = Imports.Compression.libdeflate_alloc_compressor(compressionLevel); try { - Assert.NotEqual(compressor, IntPtr.Zero); + Assert.NotEqual(0, compressor); } finally { From ef2a21311b96ba2029c95ce9082e9cf23cadea67 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Tue, 19 Sep 2023 02:20:56 -0500 Subject: [PATCH 21/44] Fixup style --- src/LibDeflate/Imports/Checksums.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/LibDeflate/Imports/Checksums.cs b/src/LibDeflate/Imports/Checksums.cs index d09dfef..2f002be 100644 --- a/src/LibDeflate/Imports/Checksums.cs +++ b/src/LibDeflate/Imports/Checksums.cs @@ -1,9 +1,8 @@ -using System; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace LibDeflate.Imports; -using size_t = System.UIntPtr; +using size_t = nuint; internal static class Checksums { @@ -14,7 +13,7 @@ internal static class Checksums /// '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); + public static extern uint libdeflate_adler32(uint adler, in byte buffer, size_t len); /// /// libdeflate_crc32() updates a running CRC-32 checksum with 'len' bytes of data @@ -23,5 +22,5 @@ internal static class Checksums /// specified as NULL. /// [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern UInt32 libdeflate_crc32(UInt32 crc, in byte buffer, size_t len); + public static extern uint libdeflate_crc32(uint crc, in byte buffer, size_t len); } From 896afd357faff985db100d1864b954fce6463bcf Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Tue, 19 Sep 2023 02:21:13 -0500 Subject: [PATCH 22/44] Fixup style --- src/LibDeflate/Imports/Compression.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LibDeflate/Imports/Compression.cs b/src/LibDeflate/Imports/Compression.cs index de20e18..3c45216 100644 --- a/src/LibDeflate/Imports/Compression.cs +++ b/src/LibDeflate/Imports/Compression.cs @@ -2,8 +2,8 @@ namespace LibDeflate.Imports; -using libdeflate_compressor = System.IntPtr; -using size_t = System.UIntPtr; +using libdeflate_compressor = nint; +using size_t = nuint; internal static class Compression { From 5c56cdeba08e2f24e7a2084d473d5b8d2a723774 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Tue, 19 Sep 2023 02:22:00 -0500 Subject: [PATCH 23/44] Use range slices --- src/LibDeflate/Compressor.cs | 2 +- test/LibDeflate.Tests/ImportTests/CompressionTests.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/LibDeflate/Compressor.cs b/src/LibDeflate/Compressor.cs index 57bdedd..abf1f19 100644 --- a/src/LibDeflate/Compressor.cs +++ b/src/LibDeflate/Compressor.cs @@ -51,7 +51,7 @@ protected Compressor(int compressionLevel) return null; } - return output.Slice(0, (int)bytesWritten); + return output[..(int)bytesWritten]; } catch { diff --git a/test/LibDeflate.Tests/ImportTests/CompressionTests.cs b/test/LibDeflate.Tests/ImportTests/CompressionTests.cs index 32573c7..f520590 100644 --- a/test/LibDeflate.Tests/ImportTests/CompressionTests.cs +++ b/test/LibDeflate.Tests/ImportTests/CompressionTests.cs @@ -41,7 +41,7 @@ public void DeflateCompressTest(int compressionLevel) Span outputBuffer = stackalloc byte[512]; var numBytesCompressed = Imports.Compression.libdeflate_deflate_compress(compressor, MemoryMarshal.GetReference(testBytes), (UIntPtr)testBytes.Length, ref MemoryMarshal.GetReference(outputBuffer), (UIntPtr)outputBuffer.Length); - var compressedBuffer = outputBuffer.Slice(0, (int)numBytesCompressed); + var compressedBuffer = outputBuffer[..(int)numBytesCompressed]; var actual = Encoding.UTF8.GetString(FlateToBuffer(compressedBuffer, CompressionMode.Decompress).Span); Assert.Equal(expected, actual); } @@ -74,7 +74,7 @@ public void ZlibCompressTest(int compressionLevel) Span outputBuffer = stackalloc byte[512]; var numBytesCompressed = Imports.Compression.libdeflate_zlib_compress(compressor, MemoryMarshal.GetReference(testBytes), (UIntPtr)testBytes.Length, ref MemoryMarshal.GetReference(outputBuffer), (UIntPtr)outputBuffer.Length); - var compressedBuffer = outputBuffer.Slice(0, (int)numBytesCompressed); + var compressedBuffer = outputBuffer[..(int)numBytesCompressed]; var actual = Encoding.UTF8.GetString(ZlibToBuffer(compressedBuffer, CompressionMode.Decompress).Span); Assert.Equal(expected, actual); } @@ -105,7 +105,7 @@ public void GzipCompressTest(int compressionLevel) Span outputBuffer = stackalloc byte[512]; var numBytesCompressed = Imports.Compression.libdeflate_gzip_compress(compressor, MemoryMarshal.GetReference(testBytes), (UIntPtr)testBytes.Length, ref MemoryMarshal.GetReference(outputBuffer), (UIntPtr)outputBuffer.Length); - var compressedBuffer = outputBuffer.Slice(0, (int)numBytesCompressed); + var compressedBuffer = outputBuffer[..(int)numBytesCompressed]; var actual = Encoding.UTF8.GetString(GzipToBuffer(compressedBuffer, CompressionMode.Decompress).Span); Assert.Equal(expected, actual); } From c5165c87a208d679679a536dc3c417cc5f4913a2 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Tue, 19 Sep 2023 02:43:32 -0500 Subject: [PATCH 24/44] Add ExactSpelling = true to native imports --- src/LibDeflate/Imports/Checksums.cs | 4 ++-- src/LibDeflate/Imports/Compression.cs | 18 +++++++++--------- .../Imports/CustomMemoryAllocator.cs | 2 +- src/LibDeflate/Imports/Decompression.cs | 18 +++++++++--------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/LibDeflate/Imports/Checksums.cs b/src/LibDeflate/Imports/Checksums.cs index 2f002be..38a9d52 100644 --- a/src/LibDeflate/Imports/Checksums.cs +++ b/src/LibDeflate/Imports/Checksums.cs @@ -12,7 +12,7 @@ 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)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern uint libdeflate_adler32(uint adler, in byte buffer, size_t len); /// @@ -21,6 +21,6 @@ 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)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern uint libdeflate_crc32(uint crc, in byte buffer, size_t len); } diff --git a/src/LibDeflate/Imports/Compression.cs b/src/LibDeflate/Imports/Compression.cs index 3c45216..21f824d 100644 --- a/src/LibDeflate/Imports/Compression.cs +++ b/src/LibDeflate/Imports/Compression.cs @@ -25,13 +25,13 @@ 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)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern libdeflate_compressor libdeflate_alloc_compressor(int compression_level); /// /// Like but allows specifying advanced options per-compressor. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern libdeflate_compressor libdeflate_alloc_compressor_ex(int compression_level, in libdeflate_options options); /// @@ -41,7 +41,7 @@ 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)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, 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); /// @@ -69,14 +69,14 @@ 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)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern 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)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, 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); /// @@ -84,14 +84,14 @@ internal static class Compression /// compressed with libdeflate_zlib_compress() rather than with /// libdeflate_deflate_compress(). /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern 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)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, 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); /// @@ -99,7 +99,7 @@ internal static class Compression /// compressed with libdeflate_gzip_compress() rather than with /// libdeflate_deflate_compress(). /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern size_t libdeflate_gzip_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); /// @@ -107,6 +107,6 @@ internal static class Compression /// libdeflate_alloc_compressor(). If a NULL pointer is passed in, no action is /// taken. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern void libdeflate_free_compressor(libdeflate_compressor compressor); } diff --git a/src/LibDeflate/Imports/CustomMemoryAllocator.cs b/src/LibDeflate/Imports/CustomMemoryAllocator.cs index 3bf1839..85d2d53 100644 --- a/src/LibDeflate/Imports/CustomMemoryAllocator.cs +++ b/src/LibDeflate/Imports/CustomMemoryAllocator.cs @@ -20,6 +20,6 @@ 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)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern void libdeflate_set_memory_allocator(malloc_func malloc, free_func free); } \ No newline at end of file diff --git a/src/LibDeflate/Imports/Decompression.cs b/src/LibDeflate/Imports/Decompression.cs index 8c343f2..c2e41c4 100644 --- a/src/LibDeflate/Imports/Decompression.cs +++ b/src/LibDeflate/Imports/Decompression.cs @@ -48,13 +48,13 @@ 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)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern libdeflate_decompressor libdeflate_alloc_decompressor(); /// /// Like but allows specifying advanced options per-decompressor. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern libdeflate_decompressor libdeflate_alloc_decompressor_ex(in libdeflate_options options); /// @@ -89,7 +89,7 @@ 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)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, 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); /// @@ -98,7 +98,7 @@ 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)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, 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); /// @@ -109,7 +109,7 @@ 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)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, 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); /// @@ -120,7 +120,7 @@ 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)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, 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); /// @@ -131,7 +131,7 @@ public enum libdeflate_result /// will be decompressed. Use libdeflate_gzip_decompress_ex() if you need /// multi-member support. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, 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); /// @@ -141,7 +141,7 @@ 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)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, 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); /// @@ -149,6 +149,6 @@ public enum libdeflate_result /// libdeflate_alloc_decompressor(). If a NULL pointer is passed in, no action /// is taken. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)] + [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern void libdeflate_free_decompressor(libdeflate_decompressor decompressor); } \ No newline at end of file From db9df5a0c80e699a22f0cd3dd0a7b6d3fe339e72 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Tue, 19 Sep 2023 02:44:19 -0500 Subject: [PATCH 25/44] Add throw helpers --- src/LibDeflate/Imports/libdeflate_options.cs | 31 ++++++++++++-------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/LibDeflate/Imports/libdeflate_options.cs b/src/LibDeflate/Imports/libdeflate_options.cs index 4fd7a43..8367689 100644 --- a/src/LibDeflate/Imports/libdeflate_options.cs +++ b/src/LibDeflate/Imports/libdeflate_options.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace LibDeflate.Imports; @@ -19,23 +20,29 @@ internal readonly struct libdeflate_options public libdeflate_options(malloc_func malloc, free_func free) { #if NET6_0_OR_GREATER - ArgumentNullException.ThrowIfNull(malloc); - ArgumentNullException.ThrowIfNull(free); + ArgumentNullException.ThrowIfNull(malloc); + ArgumentNullException.ThrowIfNull(free); #else - //TODO: add throwhelpers - if (malloc is null) - { - throw new ArgumentNullException(nameof(malloc)); - } - - if (free is null) - { - throw new ArgumentNullException(nameof(free)); - } + 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 } /// From 0dfe191a66517d435b8c892eb789d48111aa892e Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Tue, 19 Sep 2023 02:47:08 -0500 Subject: [PATCH 26/44] Move calling convention for native imports into Constants --- src/LibDeflate/Imports/Checksums.cs | 4 ++-- src/LibDeflate/Imports/Compression.cs | 18 +++++++++--------- src/LibDeflate/Imports/Constants.cs | 2 ++ .../Imports/CustomMemoryAllocator.cs | 2 +- src/LibDeflate/Imports/Decompression.cs | 18 +++++++++--------- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/LibDeflate/Imports/Checksums.cs b/src/LibDeflate/Imports/Checksums.cs index 38a9d52..816383f 100644 --- a/src/LibDeflate/Imports/Checksums.cs +++ b/src/LibDeflate/Imports/Checksums.cs @@ -12,7 +12,7 @@ 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, ExactSpelling = true)] + [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] public static extern uint libdeflate_adler32(uint adler, in byte buffer, size_t len); /// @@ -21,6 +21,6 @@ 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, ExactSpelling = true)] + [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] public static extern uint libdeflate_crc32(uint crc, in byte buffer, size_t len); } diff --git a/src/LibDeflate/Imports/Compression.cs b/src/LibDeflate/Imports/Compression.cs index 21f824d..53ea83b 100644 --- a/src/LibDeflate/Imports/Compression.cs +++ b/src/LibDeflate/Imports/Compression.cs @@ -25,13 +25,13 @@ 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, ExactSpelling = true)] + [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] public static extern libdeflate_compressor libdeflate_alloc_compressor(int compression_level); /// /// Like but allows specifying advanced options per-compressor. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] public static extern libdeflate_compressor libdeflate_alloc_compressor_ex(int compression_level, in libdeflate_options options); /// @@ -41,7 +41,7 @@ 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, ExactSpelling = true)] + [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); /// @@ -69,14 +69,14 @@ 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, ExactSpelling = true)] + [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] public static extern 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, ExactSpelling = true)] + [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); /// @@ -84,14 +84,14 @@ internal static class Compression /// compressed with libdeflate_zlib_compress() rather than with /// libdeflate_deflate_compress(). /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] public static extern 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, ExactSpelling = true)] + [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); /// @@ -99,7 +99,7 @@ internal static class Compression /// compressed with libdeflate_gzip_compress() rather than with /// libdeflate_deflate_compress(). /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] public static extern size_t libdeflate_gzip_compress_bound(libdeflate_compressor compressor, size_t in_nbytes); /// @@ -107,6 +107,6 @@ internal static class Compression /// libdeflate_alloc_compressor(). If a NULL pointer is passed in, no action is /// taken. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] public static extern void libdeflate_free_compressor(libdeflate_compressor compressor); } diff --git a/src/LibDeflate/Imports/Constants.cs b/src/LibDeflate/Imports/Constants.cs index 5986130..907d10b 100644 --- a/src/LibDeflate/Imports/Constants.cs +++ b/src/LibDeflate/Imports/Constants.cs @@ -1,4 +1,5 @@ using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; [assembly: InternalsVisibleTo($"{nameof(LibDeflate)}.Tests")] [assembly: InternalsVisibleTo($"{nameof(LibDeflate)}.DangerousTests")] @@ -7,4 +8,5 @@ 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 85d2d53..e48b9a2 100644 --- a/src/LibDeflate/Imports/CustomMemoryAllocator.cs +++ b/src/LibDeflate/Imports/CustomMemoryAllocator.cs @@ -20,6 +20,6 @@ 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, ExactSpelling = true)] + [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 diff --git a/src/LibDeflate/Imports/Decompression.cs b/src/LibDeflate/Imports/Decompression.cs index c2e41c4..bd3f380 100644 --- a/src/LibDeflate/Imports/Decompression.cs +++ b/src/LibDeflate/Imports/Decompression.cs @@ -48,13 +48,13 @@ 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, ExactSpelling = true)] + [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] public static extern libdeflate_decompressor libdeflate_alloc_decompressor(); /// /// Like but allows specifying advanced options per-decompressor. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] public static extern libdeflate_decompressor libdeflate_alloc_decompressor_ex(in libdeflate_options options); /// @@ -89,7 +89,7 @@ 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, ExactSpelling = true)] + [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); /// @@ -98,7 +98,7 @@ 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, ExactSpelling = true)] + [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); /// @@ -109,7 +109,7 @@ 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, ExactSpelling = true)] + [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); /// @@ -120,7 +120,7 @@ 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, ExactSpelling = true)] + [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); /// @@ -131,7 +131,7 @@ public enum libdeflate_result /// will be decompressed. Use libdeflate_gzip_decompress_ex() if you need /// multi-member support. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + [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); /// @@ -141,7 +141,7 @@ 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, ExactSpelling = true)] + [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); /// @@ -149,6 +149,6 @@ public enum libdeflate_result /// libdeflate_alloc_decompressor(). If a NULL pointer is passed in, no action /// is taken. /// - [DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + [DllImport(Constants.DllName, CallingConvention = Constants.CallConv, ExactSpelling = true)] public static extern void libdeflate_free_decompressor(libdeflate_decompressor decompressor); } \ No newline at end of file From 5f63118968343f2d15c399b8773b77d6f7e44940 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Tue, 19 Sep 2023 20:30:13 -0500 Subject: [PATCH 27/44] Rebuild ZlibStream without vulnerable deps and target netstandard2.0 --- .../Lib/SixLabors.ZlibStream.dll | Bin 75264 -> 73728 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/LibDeflate.Tests/Lib/SixLabors.ZlibStream.dll b/test/LibDeflate.Tests/Lib/SixLabors.ZlibStream.dll index 13dae9062db5f84de5c0d26a78c771761e18ccee..3b622014a57bb2ed09e02f68c47a660316611838 100644 GIT binary patch literal 73728 zcmeFa34EMY)jxipWuAGSnQTucnaMtrrjtI+Bx#l|X=!P?(^3kh1tu-fbOTyirlhP* zrzDm|ph$s;AXr*JK|lemin3TlMHG=ugjbXxvZx3sDyTsJ-*fJr$)qiduYF(N&+qf^ zr#bhWbGLKPJ@?$@xz964EZHs$A%uy)y?ceY2O<6IWcbmb577nD?-YpL&QELa(dPfO zcJV1|HpVveq)zTxzCN~c`Dv%6&WNp89qT#swAh-{Vl$6i6kDHKwYoV!KQK}iebg)= z=4*y{5#zd)n;0ZN|La3M_%lapXjk_I zl$8Dtg^HA^MLxh-G_g2`{jq2)D>^5&K_8Zrsrii6=bQoh zoJ_l@t<SUktT3_iXH02`0-%ZMQ&_Am<}C7~`(G`(!a{)A3FG2oYB5WPnclg2|Ff3#?Y)!nef&_AsYucrZz;A=Kg%m!VgY0(LS5!NwVyu+rzk1?bUHlGwz=6iL9C zP2H=5X@mntNebiGGE*<1to&lb8wqyMOuY;;&tKZER|Wk|Ms<;|zzmP^M*;=r>LRni z7iqH!d{L)0(1V&cc)O6=SJNEm31z4PGwL9X87)Mb+6>npZu9v$+k_0u4!4fq>vLHLkzu_0wg?+ zqYHdhNse4ABPmF)BfMPiC1Oc>4N?B|3JPp5NMiV&Ov4?T`o=NPljjmk3Ls0DIV+ZI z981w?zlAY89sve?ZweV3M(9SG5imAn7>s~%dWOLW7(E#VBVcUIFc<*?PEr=i2pDH( z7>s~%R))a{7-we~jDT@YhQSCJWGyTyBVdrlFoO{=$ZD9u2>B;NJO@7okK={|W@)=o zRivBYwtVz_i|s?V`YKwD=m!m3<{_uvOFF&03U=yEho+dBLpK*0sQ~zmwh>8Trm_Mu zhuOSQH)Vh9SI$%Q9kZ_<%fOZQM42O!u18h zL9-E}9RWJp&`ixUj1*=J%MANG%*+X|9s`ISd#V&{t9*x`7nSi5bcY+y~0 zo7x@wJaV;{m=)-=Er)|q@p<+kq>EGmB~<)s#s4R!we);yMDJ060dy#Z87;PNhIP>h zuj#5-k*rw}lszBmiordKWs#M6c6qB_p69o#R&*nhw9*JS66+57qfz`np(`2Gqo=1y zINk5H(ioJfDs|a8(V~@-ElI{mYzmtb{jh`_bsq8#np%q93_ynRcBsgk11vvr?0%+AqbFp# z<}4T_uW2B^2;(n?zeGK9&gGm{z@{)(3=FFolx72ipSALxvS!mMZx8C+nXEad(R9k% zU8MkjRsl|={RA`8ZtLE+kuxz-4~N6veL&6f_Qo7j4Ee$#D^cY7TP3wP_;saGIhURH|58LZw>ojiF>ROEk7hrp_em;#`U<%4Ryn zYtG43G(1yLZl;>H(W>kxy{4f&HN&#h$Q%vqUvH7k$+V}VwIH`WiSv{&Wv%~u3u>}? zK6wMet@*!sy7`3Ej(L(ol);dnGfb}$sWGQSyvc;fHSe#` zW~_r;y-d2vCF#Y~XVU+X&kHDvigZak3kMTWYV>V>iutPVE zr#`5{&O=|4>|LMSebLS_F=b}xY2rqwE?dzp z2cv)3(Cuum%pDMzD|vjIDv=z>`xlby6Rc>btfULIsF4T}GNVxXl`mxVKMJBtZ3fg_ z(dw5jGhj7om@R1bG*Wqh$zf{74$n$jG(G}Goa3!zDxW%1r`?}rCB1S4h${lqTANI{ zR8pL0RI4jhD3j7;Wp%M_NHLoHdYE%!`0$<)-kv5cn?rIF9FCl@)gvJh4?Vr?}ads z{8T{59f(`;NHUNlTRb|Bf$-$fdM~f!Ws)GgLYmV0lSvbCdhcow;89>ix{5TNHp-Ui z-2oV*EpjrFOwB=SHXi|djn9oFF^Ieoh&8<10QZYyQRG%2()EERb>Z8!vu(*v%7BLiq&Wd;B&RFQxGXBeB4Jg_ zGAwSHgR_lJv5#BqO)l))Bv5qWoPV^bOxu^jS7P31+IQDPw%C8#~FYSHW zOn=$Rpo@&u%vNNi5CzFug!c@2hOBeU@s^Ps%txVIkQ;1obLP5%_C~ub>0shG6e=!D z(z?_2wML8(j+C&MFkSzcx}nP5Ji~R^mCTJ7X|C1k_eZ+o!KUkU*XBNT(QuN1&!t#z zx>%1fLjUyM(-70N%4nRR1e{FnghyuTFh*$rDl|5P77^M`2}~fa%kdeBuq5D&I2++;SteW}4>Q_E=15zgO-$QI&H&r{i4sI*N-R;dELfEk zmc^^mBFW?&o{s7hjk@B2(~%=O1R|XcFWWz{4`*VSLMF1#d3@7GbJ7~Hp>w0+18v_p z93)_05osuIpfKD}hIOXlO+^7}yq*tT`qB63rn$!4X2;U!7;`?*k_~!UvPc*J8Z7>Q zDpF1}ZRhiB(@>8CJ#X5OY#74=)1^8nfF02eD(KHMY>cNonycsL8#aZGH3p8uaI7v& z)jZgKX(6GRlhFLIggC#5-F@za9ZPVkO#2O=*bB48aLOGII8_*0dc$$$_zug1Jz62B z*zt};Z@?$mgCW27qhZXT8ypOw1sLXLQ>nDSaBCiQS-$RgvyfPoIf=@^@WaGo)g;}# zHd+NzU-P__<8_<-Mnz{iM3tq<5!t!_h%7Lgg1YMud$Unuc4!XL7?R=)mqHHKsh^^F z*TOy)qlSiq9I+C|S&cv3xf-x5#g5ug2n?zgPH*vdi03l-9IFcTtGz9)V~c^O2szf z+{N_1vu~oXOa%SX+0#?#K)B?6Y)v{;96X7ZvSB^StYJPjMHZkOjOsg>9{QHl6h8Dm z;8`U`y7KHfLBAbI#`ETM?m|=s|7;F^ac-6v)tJmz>Dp&xkVzS&HG_B=B$`3O86=zC zw4DjNbuQDfyCO^1u2G(s-LUM;);whsxjw&bDxGWRIQh)7WtfwfjXr?uO*G|L5_{TA z{*39!tjesciJK1WX;=O$o|b*dS9Kw9rb+%)`izfTMUvnDWV34_ik2!n(A)|olDc@; z4VmV-NHg;c;k6Y>0ynuse~nq+jOxmxuNHlk|n#?x|)+$aXV ztWwrjKJ{0yFPUC)raa0FvRB!hWO)}{tm+ZW?Ibn=TGEy8JH-<-(v|fdM3!}B8ZA8q zoxbsmPVTH_|5XTTlBIA$LS3urL3Kuvk$sqb_$MwtuzgIC_R&kH8;$calRMUuYI^rK z(iV{C?_mW(1nT#UG3NNOKfKR#dxQ^6GCEPqieZ^L9z;rA)?B_{&Gnh)e6mG?T+6?Y ze#zOl(acn-7HYh7RV%wl3m)nYtpl5exu>hCZ!<#cydabdOY?e8GYyIbQaj3c)bMqLoK$L3pYsrqJhh* z(4shwfAMeQ6zJq;W<)9~YAPZ*jIxT#Xi-frr@W#n8jHqqSrru((V}QkSx&yn8gP&- zmsbT|tPj;Q$c?G4iN<6MZP;^TvJvFLWAxxAH#X`ZAIHdO4Ah+HzIo6kn%as7@9}UC z)?FKJj7NIT1G9-UCWkN&75Gzl{APtL zAhg&C`3tPnB~WR|1oCM@d_d4hUdaN5My$3xAQu90?XCpj!~`Dc3OQT0p+0>e6kx;{ z2n7>OE*9Y-ziXlrm>2vnPNqUua*lkKg^LGxirtNEw6BJr#YIcneu`amU|GNn`tXFy zN|rAfi03!Wa7{@>k^{)Hl5%7jm?IzvDu*Ccye(tOk^@g(?>Zz|$Dd&IA5(-AEm}5l zzJbHSXyG!fQ>bbb;hNkUXJA<}l!uLArgjIl{_D*vWd*x$uD3pP*%fCX+|8H((HrDa7bKytD|iY*AYy{SkD zn(yX0ttSB>i;{c{HL0MhbyT(vwvr5$kqVXqOZ#P_J{yria1&(|9Gz=BEE8ZsI6s`# z%s5xp)Y_QMaKH*kHSthh({mjwuwcuirKU(-xp}G@H;?O~pdgHvwwm5*$BO;>UUjJx_bQ zyz&)1oLn;npKcX^m%=sxPQvx49E{)sf1EESxa2p;fpp<$o{qhd!b;&WF6FUPaD`U% z{RWnL?Z-(S&!4pHy}f%sW)x)g^dVy$4-cTF*s{j-f3VGw1}Gn=UH=!9?>aoMw>hpq z8kl^;KgclH)&D1qfgVZo`stK_5GCY|<@Do|N7ErZB%;YD-;h&}X~>xeW@pHWCuqod z$KRCz%F~XYrX4(pKLQy;0XHxUMbrHafk#GSHwMimaOdyC_1_6*BM!H^Y`PRE#L@Wc zxCX$rSw&NyCf{O3150Al+9 zxSSzQpMkGtcrqYP1J)uaG7g#&e2pc#FVc*=MhUH&7g8O+rJaeC#ygg(meP1fOPpl# z784$k3oj3gJ1dab?BDcoRQKVMRb`=dpoCvyN%(_8*9#AzH}&UlC{=x0jGky z$H-e+%3g-fpWez!k==(*+K~kmeb)oG41AU}>M?#RHPGbEz7A=m@I~H|{903{2 zBiqkd9*qELdFB7lSl&mN7WSl;ULj5EsDF|few=AFAk~l|WvBB0Hw_6VB)Nt}+8S<1 zm8?2Ew(}?{7O07!>&VF#scSKXA$In zfPBC5;u-ISq5Fv^qW^ZyO$W=>8geD+Cm)Lz^Rm(*!ok8Ek3g`ZR+?)!!Xda-8pU`> z>INfS(giYS(%te88=YP~j!O=v*MN;& z9|nec=4}bgO#DxeTs98L(s$7z2EPq}Q=vOR;O0mWxBPXilg>q#n>B5|eXjB27HKbH zbVK*fK^Xo(i}1Q;;{~9+Gyr0@AfP+&U!}U~#EB6w?#wV40fQvq6pVmzcZR_T7+=dU z7y;w!83rR@?9MP40plAP1|w)OJ##t?A=b>U-e?$2fu3F1w|F}tr;e+k!|pWc>Q0l9 zq6RkM5vx|6N>Rg7n<;%s>PiHBSLQvOFO_nc#d@j?`$60f&)l4*3)Rtof+l|IG-S!M zXB9vQAO2p!-xuJ=OR#Xi3V$vZ@7Lk)d-yvZi}Bvnr4VsV*b24l1V2+$MEBtsBzwptr6*1V$-eqf3XF{=3j`DxM+IZ>=F76{WhnJDoqV~OF?XqM}^%eh1 z)z?t%d&NGrSN@OJei|RzX4|aHhxD~xzB<6hP{neTHOT1fa5)A~0Qf*k^20n_;0{zN z>_w~;jvaBIIr@Hk&G778vdq)*09MCVQ($PUTao7BW1mw?$E1qyLAmUr6ir^NQTeB6 z9!uPD60N#9Yt6r}%Kwh}MxB4Z-han_qk;dW`VVZt(cu4h_4`<`v=g$8@YQRSWso_q z+0WVIHNsr$Reo^n-!m4YY$(!zGfx;b&8?TiJq0m~;a#wx-WZm}Hx{UBXzr_$?@NWv zoEN1q$HAG=3si`Fzd@HT<@h5i8%-kg$fPZ@FDF88O?73aRvJ!*kH#q+ z$wp5I!#qdZvfw6~nR7&o<~Ns1GBbd4MxdN;;{jyCt4us`X6BN!P+wl6OtfpzdEH!hqtowON*5x^z zl*?uZVq?N5HSy%0uk&4TI(uMgdC8?ad^(L=AAChy#$=T7H67=j5qK@6XF5oIvbKOicc{vid1Yz!?`p9IYd zQAWUcD#Ks|jHfdUM!@)ahQSCJ&tw>kfbokAgAut7#Sj!_Xg}U1G{buDV@Pt^4SFxh zQ_+m80EASw`}LCakAX?Ls@>u-W zt`8^Dk_D8`I;DVg>cA(`XQOq+SkQhOf4{@vn_~%4h12LCVCj8P5O|&tEAiLyB>?CE zr>}ZqCf?lRme|}bI*g{rFL(ih*0I>d=bl01c#+nuI$mw0Hq&jhs}VL}v5Q?OP6BAy z(Lq3dX7Oi}7wHb#HA%XfcZt6mFVkC*W$8zu8Y{Ar?k@5Rby#Z)4}TZn508~a2j0Bj z>n~p!#J#;d8@c>^=ohrDt~{7KbyUauSO-c&4mm0eL@vY8du1r7!aO`{47fo&@wy&z z*6TRM8E^ym3x+@M=7m4MaX#4TpHM%ky7Vep_)c1TW_ab2w=I_!b(m@NoSgC5*q7=D zN1OlaT@#J9UF<&zul~so9{6R@ycDN{s+E@=>{jZklJvvSemI^N#HAfVn+o(^vJTq( znbircOzOS}hu*H)ba2eon)G+r7!5EQSnaq~Mgxsb@IJw)bFE3Zc7nUA!v_KS{u2+} z|8~s>r~MfBQrGlsw-rMkp@ooMoFIpdJ*gVX>dh&~N~+^_yv9ryDDW;6+>b28!oy~p zdlAQXYiMyh)PY*H?7gGG{ttMZXhGY>WM@au-P}d%S|K5@HiX2~|_mWzpftAU&`x7HRu7L0kS!^`a_O#wh>yL0XSX8Sd^#+LV0hb%?kidMvNae7)ZqQPZ}O#>T9w!Xfke-L zpp&e&OQedz1Eux#I_oLJ@NfAZq?Qje@eC8KAT1s!7LOuBg>GTAXiQV?#47vY03GiZ z?&x?Cvmde)H~Rq#u&S;Ol zaK{L%Xg2A5)yrQvdZ+>|?{Ev~PESG8tGb)dgR$didJ4>gC0x2gEq{ts=&G<-g(0^n9CdcMZnz4A zjmB#rreSZ!t|_8?ld7`Fb=_hYuf)j_cwJ@$9$@%kR34NO24TX#Y;dy|m!N;x%)gMf*W{%5 z?<^qXf<-Pknh(mF2p6$LTrd!`U=1{^gX^EGYY#@ zUH1*!#WjqVJ1d;`g&QIbl-CmHV7ydNUVe#OZN&0$D~NV$c*&EOl6di#X&WaE^7Zsq z?271>AIxI58T}`ohZgC>-!9+h)pNClY&0+d(*{yXYFjynH`pv$@qFH1I=sD)X9QL*tsp z^67+h72#AHb)P(MXa`E)_rQBE+_$N*gh$eptKorYNIjja&pw@_Rn7q(+R+@!eeM_c zI!JaLk#Ia@?TCZ|Wuc-(rCaD0xfb518%Pq{EkbccL#>R5 zd`-_|7X@`lRUlDgc|ic#ELCbK7ANrfbEr6i z_7y+@tde*h+TS|X2JFJ6RETc5#bZV$B6eg&9I8cg;-R8})?yp@0Fodfvbx1>zs@MN zxPZ4-J7Icce5i#ST;0}Zt?G>YuX5)M`9bK~nrwy+4B!Y-*Bk+;LU9>Vm z>3L;1qCg>-`MX@Ji_#Wgy}}Ha?uVDNn@ZryYuF9$pa!A2Q#2frP%eM9cX_ZxBR;Tlju8wsDbVm`<+8yq=Nz%A61I9 z!+Yc880rYM1jSJZt6(p3NfcE6026aG6b+S>m!e z6p?5^L)u-GdToYgX53aW&JBG|Q&^(WqkAX4#~mEE@_`S13qj(HfN+-+EANQbCrD@75_c zDFq!~iDwT>Az3y0o2LYPvvV9~13taR9z2|($AcO4D{6eOC*LPFY@7h*CM)dU*a0N3 z7_YBS1me&Cbv~D6g?VOq-f0k85w?SP+j7!;`UHin_fUj2IrU=Apz1BuEP87#K;IWo zLU80{ko%O4He|}KX^~~u=o_aX;Uw0-37x9bM>iAmP|QvHCYa~l zyg%!U`?BBz4C)sVMc;(+Evv|~ZmbSg6$vY{B7m>a;L91|me;JvD(Ifl(u?jfq%92^ zVgC3_FMUG^uVa|iAzuYfzfy?B{OmLZVwa%Ykgpt<%P|{qVm?4kQ;u+;sdh&@r zgtd?z55u5dsD)r^7A(;FI?;M+1hWYaW5>YouphU)Z*T&lgVEwZBI38auYh0?S?f)h zu(6^pAM-LE++oaEl;_GI!umqih=wA95QDZ&6s25L_Ad2QpH^3R(z*80Y zfKxnwvFrEaJ3@(kl?NYHLMD3Z=(pOw1$pF`8h{*56*`GCKD;=mtPSS@K3t@U=f^_< zEFwcDJ(nR_B54u^ldwr&Et1M0X>k3B)ZjY!20%Q8c@8hf;0rwg3>wy8R|>umZ6^84 z+P30B&TN#X=%x6Qn{N0$EQoRW9iLi=jCqT$Fj*DJd6?5Hl37_*RirD~^m&VQBf=Ru zH5N>$K8$Eg;-n-}rBc%350A4KXWEymazl;-R)6w|p@x&H=U^b<9p`CqZIR@YN2_cq zhihVB1=CQu>8gMlVbW^H%nGa;Ke?2aQ6DNcHw$@859gBD)8PiNg|GE^KLCX}5Z?fX z{nwHG4;dGpj%RHvXqjXp^YUbM`6gE0W?~^;i5pXE;P^d~uiL}< zO*OK#3>OPbESI@R6xe#3jI|fbv1y3ekd6M~(~em~)};Kj|YrtFYp=3IX*Kv z=K~%046zoF`(U5I+#*Jzz1T_ew`IevOZ46aaM5M$*!=E+^vV$Sk!N(uL?6`)Sm4ba+ z70ssKYM-e`eEfY3`Ee9qu}=;;T*Mp9m)}Oo@~}=EgadF~%Xh>yRxhBXLJ+4$(1OmBI2B3IDgSnRXB+ktlBLAKAI!p<5CO? z=Rwt1ETs-%$aIecO9xTUV~i#jm9huKNMappU*8rW&utC-dj zCGeIx(KP1Z+^`mEdxgH%TY}wlZnO{4*sS9T#Vb-j94Q?yJ?{@$>`U>s7I|FB;ZNG( zqYpSxlxGLJxEnItkU4hVp1p6Mh7TAgMq81!%iy1RhRmH>Y@80#2x^eGR8|G>aoyG@ zWjb8e$_~&bPw@gdyb{IBAL2FIIQHNIqp@HjdY}qbJWo(^`Sx5!2*^!3KMx@c5q^!d z%ziJZ8@!q6s^qTIVG;`SRdiJ_FC0k3bTxkueT|alLfPHCfbg|FpF4c8`0~ETs+0qb z#u18Uo*X-V^d&}8t}KDlJcDPpkcm#L4p|l0{+XGzm{pD;9qjlC+Tfa)li_8vpT;C% zXxT+Odu%_)*!Yy)Azz@BK73X!q&pUyHXYi~amJg8Y$ORzt==BgeTk;w$Gj;MB4aYt7W#hoUUpB_k*zXM&58&dJ72e7vji@UQm;}AG{=^To6yYc$ZYJ+~{o2^(U9e zHLP3ESUa?MgHa4E-((_M!b#^wKQ}P-5L&o6`1&cm8e%0NDuo$o<~F{WK_M7EQ$`fV z0W2auw~Y1;0scUligfVqJH6!8@-;NJV<+iH>3wd{#s)tqgu9L8B`Pg$p`X6W#vgA>Mpir#4=y58$7yQoAf}F4x-;B_sg}k>4wn{N ztTdVxc*BKHlzCr6Up$e93FDv%(~Z&kt%l!Z4f55{^H7In_EC;Ns%>jXp=lbjp6c}Xnis4Wo6ASoPcOo;SfnBPSF zUc&jjD*adDnj7)J){2Mu0v-aoq?i5&#pt~hq;Q~PrQacjk$w*WU98N6?-Nb$0x{C2 za?k}o{HBS_UPMG7(hvyK|B8(>A;hNP!6m4QBo9YYXhop38BeL1zJl+WE^8=l=%Lqp zj>AEex06~$t97h_65jKqBRNj#mrKw>7l&@MPzrtfPVc2A(oa7m=qrSYpnou6FqZGo zw_B-wR?2{c61P*nEFP9mi8xK>RIe}_D_>~A3i@3X(R7r|E_Enz-NWM{6iWi&Mkp(t z8Yf@ok*c9fnx*Y{B2IEh&0~E(iSd-7Bi znxT9+G@VP;z}m4D*0JlbL$qy_HUO_EnqdQaFYe))lU@dSARCbNSs&8ds<&ikb3fqb zAnCh8YF`;n1^=u`o*ii}w@HNuRxppeA@$-rG$RjS8oBCBO-B1rYOP|Dm4v6vh3<1w zO`J${Bu&kv?HR`82S9l&G6Du1p=2-u29{Nl!N`98=5_SNf&C4~(AniOQZ)W${0)ZA zkiU78EGk9^x@ou+@TI>YT>2Ya;?4M*G;aTrhoL}v7?T)A8m(mylL_IDq|A)Ksc?>r zhhai`7tQzQ+MOQ1x$ z_XdO;?h^LpOP7%20$9aTfkQ69`y+K5w*f}X9Ve5JH|Sz5>AhSvZsbuF()Ho06BCpQ zN0{(2u-9yKc*;n&*KCrBC7{ED`DCkb7gXm*xC=aI$@Lf5rDgmDESLQS7mO|-e?hpi zHR=iV6a|BW@_o3!sKnY1J*@l%9KSgUI)%R&-kmgO$d+aN1=W|C9SGmQli)8XV#r@e ziaKP-UmUPE_Ui!l7a!k6(qB+D+&vk8!3^pm_7_mT&RYL9?c=G%&u9JU?}Ys)`AdYM z7M<35Vhs6FUXmF6V`B6!#yprUexSg^qZ|y9vK_ zT#bVZ=VB^tX&^TB`5ltMinF9Gv zQX4VkJ5IEZmhU(aKvT`WcGGqup{GN{gZQM3#aO(fZvFs z171iPNaG*_Vr`~CW*-ZT1|dm7Ar(g<^Hn=nA*6qLFHYOdrll%B&Q#S6C$30(P+f|~ zaS0^Qxeb|{QQT^dBV9@Lgj2^79!XQ-c!psL>=Q(5j$WG(Z~fiAu)y=1sl7-pdq(}BIa_$W(uT+asI}|X2SVI(}t9h*}4Zgtf8MTVn2i2R>K2& zFHS+srh22XN|yz)21p87b2zn>@JO1{H9P}5JyU4nW{v9{$c6+uYLJ z-qPNROC$8biFJTS9fD_sTZFg|;dbPyTXaUxn$u3+NGax@B*ZB`@Qzs|cAv_>10d?= z95Z(&;=T{MqzG}-*QHiaV*G>0(vExKrqK?(z4(XLPA}L}?UV3Fei8zUqwu#Bf0#Q2 zw83xH1wrFKJ^$g~C`O;sZ*Z`Dw<#P|aT*h$t8la#|IWlxg8o?|QRdWHBCnj_Weo3$ z{9;5%%&4Q-;<7U;Eb$ztIivj6s3pEoMsOD6_c4Ab!=(%xng3{n(!?0{M@FOMYooOz zEzw^_@Z`vn220G3P>s9F->bI7s`~e;1L9smxLrfAvy|Y?-~_}YrI(Hjh#zxoS(x~z zkEFaWS6}51h=-W}SIBIMzp@N3MJUb0=v978Jk4-(j5q_a&5-JNZ*w#tMpgfQM1gp_ zgvz;Z1jU|ONp%&CB>cy<1k15HB(H6vt`W@45B?P$!6hhJUq1Vs7k)>T5-%*1$?!1bvQ>z)q;qdgT3ClVum(_Qzjfrd6+gsD@NScpt^I#5!yU00O}CLohufC3PFPo zYB*OMr!8cvei()4XE|{%8@W{7FbYNHr2GcT9V+u5=-5PY5S+ET*DfwQo0 zlmA4(@sNLE-!Y6YXLz(loRgS;0mJdkpB*OtA4&<13J@G*d?BZ~jdSg0xPxIA;IVy^ zk@wiXzcch9<*|J`7@o&)4D&DH^8cb!nVKN@xIwUt`LztEaxK4R{7(#DW?0Jn!x*2% z_5PXhM;LzquvNrM*8_g1qQ}C>t~m>ET>XibAqvBrfbT9QIIoiS(q$DV0`80x+*?I3 z#GJk5gs*1)->Q}ZPmUta!<7V|;WVw#h9*`r{|JUxIK(OCa-QOpgGgzJRP}Q3zsI>A zWzHpa#J{$NU`Iti;E~XCncpeF=uO3_BQB050t7W}RQidfveJ zsSM9%crC-@88&d5wcHnHa$o$u>MD{XNb=mzGE5H==L(kXTC`ddKPn=4C*!|je4vQ> zD9!K}oc>3geh=e!0AJWA?U1Z)Vc%&CLtOibY*#01#4l#JgKcak!vdD`I8M{eu|bBP z$$q5fWh(|*&W8%G1OM8<4S=_>hC2$Vcb2pK&lcWjwTsEM zS3!mWu5o}%u3=n`xQm&SX1JB%+bsW=I8Cl3q{CWquTAi$4Chr*UEd6m-ac1Pa6Vh( z`q6~{rj9hcy6#JW1EYvDF;1ML*^*^j?kl|woLwWn3iuOlQ6=}zag2Anp96m@$Jb@M&IX+F*0u06?V z{$g6dqevqquNAk{2K}123D#08u7^Eq;u^-!hi)~ojq#HU3;nfXUSSBZv5?wG)>SLM z!Flgyd;#M}1J}eBhGd7e;^G48shg3jR$Rp8yjeh{t!4h@+}=En{Q=`ALRYooMaF9| z?rOz)SZS^3s{JNP-jGxBGX)X9Cf-FX*3z*mtV=h92&No@zptr6Et5*3fJ@_}0B;GB zJhfc%?dXeI@jmdQVptrA3(M{tK}(_JFsgePbqZ11)#CHvX4HFnmYU$##iyCNLcAH9 z;5Ws#5i;fE`e~5lCZf28`F>m6m8FjH2gO{ht`KKmJi6OoBo;E&Z(lunHK-*_ZM7Fv zEWkcvg`$>J_&}YisKVL>xLLnZQB}1*P@9>$(B3ohRDVdE&s4wt#K;YxE>m%zA9U|@?o(@QGXb<0N>5| zk)r-F$_MHRMLml$%fxevdI@Eg30heSvDLmAapmIoiuxMj%EjA?Dk$mqSBQTy)o)jp zTnNg<`5)zbCDiY)6nRYb+kXjN2&z=YJ&L$0QLW;hLtK^cn7U9rUAN6&EsAjV2+fO7 z!{_`l@tUII@m>BpanEQ(X%~tWl^Sj%ewsFnpneu3>X3w_9;>>|9~Y++C0@}^4r{o3 zwVA1Y@uzyCR@1?=)-N6?zd`rJ_AGU`KOyc@R1dV&D1M`;%2D6+Hwpabe0Nve>u(l~ ziW=wh;S{@*sSCwTp*{WP1}hl?mv{bFO)oyHMj zvZ7X31?&Z46;a4nQTnF;C@~Y~2_)zB-kW|Lf*vZVg20>pqeb3SqOK_W#)x8jkyzeI z)K>U{FsK`{)Cjvv{8Cc(nV|*P^Ss1Vzr7{o1NFL!yBL;yqWFu7y8@PcqIh4$c_oeZ zGU1yh@UP!)EolW+z!XLtMpd^6tGLH7s=7r~#og#E5X;3V6?ePi1J$D9uCHGpR)`K2 zcXPcD)HD_M2=c8Ib5-24$hT4)t>Ui5NL?kCsJI(pgR8{KOuqO8d$l-K#XS|j+rL_z zq2j)TxRbK5?luCabGC*fl8^khew=f zZxUy#xSx$!393)U-RP{bd&Q?!-0jYKP*WNo3SR57PqOm z2V-{{Y4LRxmyRzGTg1I8Zd=?3>ia70%XK~WdE!wOx4Z6aP|q@TwK%6fZTE>roYmtq zuA(`9f&D2l7Ds_Zc@3Yj&lis?>K*$U`y%l?9fVQbsB7#`3+Du)q<6bm%wS56m5W6; zQ#4j?urC(xO3MBebhZtLDU`3@z63hkCLE@=+P>-qcn`o;R6#Yp6;Pq52QX?sBSt9d zVcQ2Pp{PyXE%xQ2ovD8N0`Cq`lU3YCSowA_Q^jqDm2VeEsJOl11>y>EjG~-~4^+3L z#Ci2!v#%6`iYkwP#~u*f>qzRYVoJje`qg5aq9%@d$i7xwuc*b9-vJdnjZ#XzTqjOs zO6ui0v7M=_#M#9^vab`z_fX2K#NXnB_LsyxihA6B)4p8{Y?N__1WaeQxQi)S$~|KK z859R8oRD*m_`aelA~EMXB6cR-nZ8;y#~Pjcgr}%tQ1^=#MV%09bsi84L16^gzlu+A z_K0PQ`VLd;73I`ZT(6>Tt0d}TMYYrt^*Kcy6(Z_ZMYS{aZKALystZj;+zX2OcI8y( zLGforO-J1Kgzqd$d4+hVa;Ec;C{xsri>Er@7xjudB0kmmftaYMsi1x+<}2znP!Ef4 zMWw=Xogay9L_rg`l^p5(SnNKVs4K+vB}wO}qVpUXcTvfS&SPTtCQy*`_Slim6XK{| zN!cS#ah?`8E9#3?JU^etXkXwo37>Y6qMoZF z>JmkTB1B!LsQyaIH=wBRM2WgiQ4dGggSwF@?T6y_@&&@uexRsr&gr_P{g$bI(H{Q0 z<7g``rZ)UgEWnyCprsTwSY-qP+F6SFW951~PfIJRB65SCubt1-_jwgmq`k&ezoIEmFeO*%AN9EeRilRO$*B(+7^-;O@sG_KkDzxVm zMSWDEy~332qYCYJilRQM&|X&*^-+cPrlP2iDzv{Tiu$NRdtXu1M-`fh34{7*clq7^ zO08N^q}?j5lc|0&IXpTL)vmaV^dkExrtMG^^=3@_qN1qB>a;r)MLibRb~7dWs9r1k z4Am$5Xq47+Ih9E^|F$zqJ4I6VteUofr)^NwQ8g1lovWxiuPrcI>sQnmZvv=G6je5Q zfoRaKRFsF6PJ?!xqVlmiOlUVLsv4`qgm#CbNY{zqVU2Ks9B1tjW>yL+7XfxFJtdCPCG73`9Phds7u021LL(#ih9R>$ey5GrYNsr zMPPz`=g}nbtC!%hiiXT)bS%O1ogh64j<75%D!qyvct7=m?Bv&MO@zi*Jtmk zy~SRjeOghMBi{mTyQ0oR4GXku6y-6sQ&Byf?^Z><1pm1}yGv0WT*^I6$yLw-ZRTe) z?Yk0Xo+>GOTjc_=NZY8Wvl@t^pVy=_kLx&YtD;6kDeh85{SNEFMcRO*&>|nG-w>r; zXjhH6F0fepy-Ilk_G62+KPzet_G62+f2x#kSMLfOqvc;s^+`F8)xu24{ySEi%@pbE z*1&PvW=YwLN}9y++WCq)66?t0wQY(j$DZqWZ9q|G6;am{1$+5J@wWoUYhPBB2;C0~ z5#otjf;vuq^aJ2qM-%_BDoXR)YQneTyu%RJ#rNgxE~cl_#P^BGNU4cml^;wfYo{0Y z4Dk}AqNgh)&$I^O{HAh9p4E)cz|%mTKNet!(cH#g#E7$Q6s4z+2x#I2hv1jIec@yL zpp{~E@mLAPQd@L!2|rHiYP#t@$cZi^&Q+B3C`fjoFZ!tgqV zQqFnk9$h>|og{K`Dp}`RG_ECsk7M2LpLYN?YT^h;tK;0AaG53-PUIfEj%B_AxG7eS zrv7>ueP@X!z;Ob-UmA)%*u5!jWBU~X2s;AZwZiv5!i6gB-TI0WJ zZ!+(dIps*Zx)c_*zg=m9_?a5#!4eESuluB$k#$LXlem=sN6>A?CNG73X3CebWQT}F zzZimIP1b-TGAZ*eRk0MP#L}1%NaSr&>_H;bGp%Q)yXmT2#YRhN1 zuP;Yw29E`kd*3I>LM%ZR;%D2iIhUhy?cZzCLSE<6q(8~UpD86tB>W;|#`jj3Lt_L_ zK3RtnNP=m2hXxEW zhx5w*C5|p;vKN)(ZvT|h%k4iZ_pe<_vVZL#R*7<8#}MOz!-mmEKJjOpdREpV+bBnwtZ@=s$WqMR@B^?$Q;bIc`b4ZM zAlBgHMQd=+Yz>~6tiksa*Wi14YjEFa4g3u~mvRB=sR-emp7n8 zGVEfwoFUFuk@9qg=Q8XEEXA#9;$I6`DRu#B;>!$Ojqq~7B2f!iEAD1~o><$^g!??< z&=lO@>&M!yOWWZb4R}HQ31Ydnr~VYNMyrUf2WM$L@t4+Y1l+)IbVa}THkVT+v{5&U z)3s04dkfUKCX$482iY80*kq#2#D|V;LXG5^5Ul4O_V_Ba!P|eWbW7_9?*K^;hb7 zjB6V0G#}(Nk1)JgTU>t&V(%RBRegL4Ar@;nh9g-U)QYPN(#bBw(#e5_ zCqZ`_RpRmTCbW)9pxW`|8Sr6);LjNT+?dZTn$Im-jFj(ai@8O|Gk!ed^SLdnIOQr% zxsGGkaqKFNJp*lNF&)^`TY$FsD`1}ZhiT(?efI*E3DaleyK4kvNMl1Tf}U`|X%q#3 zE%+{z4b7DSc8F@g$zlXxrx*n|Q#1k|#_%Z73Vg8`5115(03I(o0lUR4z*Su1DO}e& zu4Mz)at?DgGpCO^=X1TE=6bhr$=k&|lrX?GUcEF~z|F%xCc{;@w=@eVSv1J@vh0m^_G@W{9q)xr#>C`)oI`z&vo!Y*E zIcIP==Wscjxtu;O=X@^b(_GFrE@wNJGr+m7;c~9mZ$LRa^_u~2*1rsRtNvBM=k>1x z4syxA<&t0Hl3(YN-{O-0!X>}UCBMfd3xgyy4U({%;VOe#af(4YJl~))KW&iSwlUn! z=?6IW8iRCuy+L~3$+0&Zq~}`=(&`-sY4t9H^st*_?=h&??lY*@_88P_o=LSd0*=B7 z{99%-R%`@AVnMJO9`-oEI&l_YLYxQKE*@sslHV*25d#IyVurXG@NjWA;3DxD;8MI) z+KeY@A;43`BEXFdd&PR-Tg8_PXNc=i%M7d$-!fM+zLN2+jBjQ9A(mv2VW&pvyBYQ| z+{thc!$F2Zr!+B!bvm`8lkpk)O_Z1MK88CP?qN8{P#Bz-VI9LxhTRPN7;ZI+QO-`r zZ!&HdcNgwqe30QwMg?MDGQKYEEfgk~#;}uNH^ZF{@%JzsWGDihl3^#qZiamfcQV|= zaFC%0a(afH47(ZjG2F>;55qx*B9GHE>}1%@u#e$RhI<$eG8FkNA;Uq2FESJb#Mc}J@ugA89} zsD&tREyHOH_k_p}2N^zCO0h38)WV#S;WUQJ8TK>W#c;5k(ufLzF^1C^E@#-!a2LY| z8NSG{yNc5HG2F>;55qx*qMBlNGTg&(kYRt6_`4WB$nZsmT8#Le47(ZjG2F>;55qx* zqLxx>3}Xyy8Fn)4X4uDYC&OI~_b_~r;UL2o8Hy2H8p9aFT85nrr!nkixSU}h!+wT4 z8SY}Zhv9<^2N}M|P}Fhx3}Xyy8Fn(9#;}`VA45@3>0=B#8BSxkoMAu1oecLd9Aqd) zaW00P47(ZjGu*{+55or;4l;a^q3~EjhB1b<3_BT4W4N4QAH#lzI~nd`xQF3`3}0l} zJ(@gN_h>44Im15Y^fBJga3^ziGQNlLJ&X@BE*hwWUX0wC`y@ z)@(grZ`C{XrTQxUGX2~7{rV&N&-6F+cl83J!Wd;V8;2M(j3ve@<9uVc@qqDyVd5(; z0Z5UDy+=OQhy{>61bIrZ7AzH0aJQ=yJCeEBksK!~#S)}gj+UQ<)Ipv2_?=wZEAbb=d5B>@r_UcvY1T9TCg+u9ex;cBPmOp9@J{D7z*kw? zAH-eDhZCQCAc_5 za2CT#2UE51GhD#%o@!kA6TKtz0jJcEBo!XPiye~WS5*Wf5rTD9#Q#Z@;EC16NdH~d z_O+o(;QzgH7T3g(x7{Py$hxX!ZA(dxV9u{uw^FJ=X&fovs~rva?Qj#|FT-tsR+#h< zkCU7mMoa*wd&FeGBS%ol->)T&UB&oTj%{Rk7V}>%r@WInc6%k^n;Hn-z<7O>@ISKV zZy7NSrLAYHc&B;}@Z(CT#zkCvHQR$-^>9GPipqcu2LW|hvJZGZpbl%sn?0fsP!~nm zNz*>s0bY!>G?IhBOOO^f`T=!Vdm-=$ppIeb0xt*DVf`WCm4G@%Kq>HQKpkU00z3++ zix|?<-n<<62&C2E4Jv^jfwY=f0H}*2krp@l0d=vEy~ojjI#!nCITi!z;u!WC#{%jY zbL26SfV${nZ?ObW7fZ2T)v)hM06#%A0Y4E?$M-D80PhCWF-}{7uK?7sT5Siu3Q))I zYmWnd5}+ z68L3+I&S|g1b#UnPR5`iyiWkAi<_V!9dEE52lz#3MHgR!K6G&lG@*-Ip#vQrb2;F{ zXt$0X;A+4}&{kdi3|>peSIAFAX-@#^;z_)jMb8CNfKQ{vy7)QTs*7jPN?rT{?bGpw z*11Uk93W1X&@!AJ1M1>sv`iDP0P5mZdtMk$3FA3z|eV9cp)I{U)u%T1%&-;Hvta;!v3`{0xt!G{cE=Xj{w5{ zwJ!rN2Za4=w*#*Pg#Bw@0c_Fk1Z>sr25i&54meKx2H<$@TYwX^Zv%E{_X1AT?gyNt zeHZW$?LojpwTA$wYCiz%)E)+$rab~UUHb{(Ozo$Dv$UU~#@T?nn4>)bc)0cy;C$`p zfJbP*09>Fwhmo;Gd;#w;KB@g)`;*qJ|H*jQ_`s+zN0_6{Q%XqYKENvN8o+Ap8k`Q* zXhrxGK~&pbreSAZRE{yLZ3i5o-33^u-Gx`sM`|8kJCAE#yC&+ja{xzadjLIc5O6e( zm^3`qH~V-}$2=&*1?CNW`p66hlg)c^>Q5(ql!B*sG1;8oKs+A-^>3E>6wU@^I-33I z-(>SroNm%@sRZCh0`NNl_>d5MP6+-Z#Qui7%?SL};cq1V;`pn_-zfZf_#2JC2K>># zLfn^b$Fr{q_?w8osrZ|QzZv+Og;97o!UFLPeS&yNzYO^0_-hBwnU9`z)ROkr7O`yE zqBEACv1aA8o}T6B&OL3-8H>-|uzJy&O{=H0wtZA=dyAMkcgErtv9V`miXO5jQbHeOdZ5^{`w#}G67W~;Q9b;RjceG5H z-r6x^+_bj#*%KzV&Y0dRW*jza#^J48a;sQy=DKyuSFBsz-6~kRqf)7Lz>i+NeASVs ztvh$ohUKS$JYx0wRL{9UAV{ker1c|OHLkTyOk1^Xbx(Vnn0wk8fLv!A*V!iXw~5&^ z+FHlY9tYWGwM`s5VP;25%Y=zzXU&*7ZuW$6ZL?-|w4;#|+Gn)Q8aK0LX3NBJvu93g z8QU^@+}PF$XnVUXw4DoWmxZ>A>22*Z$F(9+TMN`MeOkw~+0#14PMF;>ZEVY|3F9Wr z7z_URY2(Hr;e=VUr?pO-F@9$I#E!B5UwhvICf8A&d3&BcBUw_9EU>{c4YommJnr|y z*v9>o2G7gWW6Kr;qubrLXBzc%kGp$hjd@Afyd9Da39u|75C=8{7Mz77Bo1K0E@3x; z5E57jVMCS;3kfWQ-MmP!_Ww^+b(b_PIV}6_m+uQD)xCe6I``aDr%pZZz0>J%I2#Vf zE>Ws*jvqv=qlm`h|r#c(X0O~s=5L_C};7P7f;4mOCT zkrhcqv&l##luShv#dIzni5HNmiXD?;$5gRnPMF%GOrb(37D;6yFkdDUF2rNmP(GRs zg;JShCR#|QbE#so7%M$WO*X$sFoNDH3428?b3z<+jUkqnM@hnXFW@EQNbO%O>d0cq!^xmIM16i!6a;XGVDlFY<1nH;PZ zi6vsWcr=&DqIo5wkys?-OjN@udsd_ELGD^KS0(L^?#Nn|s*d?A%96hhey z>L3)(gbMk57X4Nx6OBS`XK|}rT5q+xbET}4D<)#7=QM7Yi6?S}Tp?D7g$tovDqDn}{^Z8gJo==C8;YcR$B%_&3JcG893g=RxSUQslh0xT{=;HBMAr(id zW9d{Pk5a*%GsSc|QAD4R1kTV#&fLuW?83rrnWJz@r?kAVRLDEW^M&GUW|^q8yi~}{ z73RTMSU$RtcWBpze8I^gqu}J_xSYw(0x!%LoaMV0k(VhIoZ`&!b6C#8vOtJ| zjWSEiPBBwj#$bR0R4Lr0Fwe)Ac`VH?9CNY|>cHrC;fC{fJGq6$yHVVk`OMPYP9E`; z+t~6}SJAIky}a9XopRY(^SZ0qdbjL!447%w8t8jgyKT?wtX8+1?nb@hG%CVc+-g>w z8V{PSv<*xlYVMO?##MQ*W7B4Pw7e45ORH;o$-bCuqem>(PkYsD)sgdZ)ycb6xgX~) zqO<^Bnsb|i8?Dw!cb(bgDkdn3ectmFyMV0M>6SK0s&|U@X1%ixNEt`G?vjTRYi28% zYi(}hFQ`;b-qCa$^|hwHptO!Lj@mW49zos{>y^4Krs|!1y|dZs2nTP261TgOLaB@m zwW(_?sZ*}s3Hep$Cf3R=Cs$XqZsjEY&P%cGH9%zr?4+{XJE<({PHMkQJM#+70T;}9 z-SyTWx_g?Qv{MR(u>sU#W2>`1IE$6NQw+RC+-YD=R=jFp)-(4N?~^iZlM5Iu%ZL+iuqF49M!a3tHUH3r!ot_&*@`Si3%myy!Kn@E0yTc1qNl zJ2Q^7TGd&vxz=6ZnKxgrco)l?b5FxjFP4j}lRM>cX4r-qF{hE$&Q1;*3xD3ob2oP8 zWlmuh#Kp`^b7$sqtLrufWz2i0yMyS^e5VxEFAdB(>baW(GZr>!g`KispxnA!ACNQO zI@WeKcV02y!cD4PduNWWz3f)4HYod8y;*JDd$HU(w|#QZRkDN|aIaRqi7stt29;a9 zSfKV6iA-pIkzDj+ z=)Tvscj{?s9Nu(sCX{g2y>I8$N4IL-)$&#ylLAg$FmlUmh-aqNpe-Z~)p|?Da%V%G znqIflb(>YUU3I!u2Qve9ysUD!S@E3Z_Nup0@2*x20SspDxa6S=^2i0E8sOlF=TXl~ z7@@pg6hvo_K@IXQ&2~iE{}>G%|Zr3?cVZB@BtH6>@F3r!`=z z2xo9qXD>4iQOry`L`E+mT5UJgw8LcSlpZeVc1D8CK@6KpP=c_q916&Gp+JoEa&E|x zta`el1a>K!*NOis`}Jtzw@#L>ATvoviYwWQSZ-)~#ce8NYF3!4a%?Dha@6amC3Ls- z4R4x$TE{+L$!nj&RG>3`N3-MBR9W?ONR%^pLrsy)TzIMBVad^~)z_9eY@-gkmGxc^ z;>@73F-4ZFt{m4f!W@NLf>)~`lV44$Za0@<4xQRmvmKMi;YDLe+0?MCh7NNH1_}-D zt#icG3AO!cnB2CzF^`1+ruLk;=UYwBkr6?VxPzYXsfsa)ssWhBZ+V;$!VX=pf@XxA z)h-E~uII2oaAXQ6h;#Khk|?=1VRXopxs}-D+ za~qW{uH}>ynUx6%;!8Gxx3#C6!f_tqyxvM=a9S#&r5JLBLKKG8m`Y?i9dD!D*hV+x z(gc|<{%&P?3zrr)%3ihVRr?gN2a|DPFa|E!cG64Y*=&*JWPQzZTovUK=;;+*$9`VW#C)KM$_wZZ*1S zGG(vbC#(9Cn=E+^_q3o+Ki`ygv5lS1N_S@lSK00Q+PalBgHc62rWnMIJ?7?iKSTEy z)C2b}*Bh{ll?y@HZFrt|rXXxY%DII0CoNVS8lA67oMIyulAhFXS>@glOj@PO=RgkdI=dmFWK-G|aD``;rwvG&DtNO-fgUfW#+9j~+ z!YcWhSCa0RNK6UsI#qQ0|;&5yd

CX*NhvhnQs;)- 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 literal 75264 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 Date: Tue, 19 Sep 2023 20:32:26 -0500 Subject: [PATCH 28/44] Update test projects --- .../LibDeflate.DangerousTests.csproj | 9 +++------ test/LibDeflate.Tests/LibDeflate.Tests.csproj | 7 +++++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj b/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj index c0837af..7fb0d87 100644 --- a/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj +++ b/test/LibDeflate.DangerousTests/LibDeflate.DangerousTests.csproj @@ -4,12 +4,9 @@ net6.0;net7.0 false + latest - - - - PreserveNewest @@ -18,8 +15,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/LibDeflate.Tests/LibDeflate.Tests.csproj b/test/LibDeflate.Tests/LibDeflate.Tests.csproj index 5c6813f..80c11b0 100644 --- a/test/LibDeflate.Tests/LibDeflate.Tests.csproj +++ b/test/LibDeflate.Tests/LibDeflate.Tests.csproj @@ -1,5 +1,8 @@  + + + net6.0;net7.0 @@ -9,8 +12,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all From 905c127f34c986264f8dfd4a510f9347e7e3d384 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Tue, 19 Sep 2023 20:55:01 -0500 Subject: [PATCH 29/44] Set langVersion to preview --- src/LibDeflate/LibDeflate.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LibDeflate/LibDeflate.csproj b/src/LibDeflate/LibDeflate.csproj index 6569ea9..815a692 100644 --- a/src/LibDeflate/LibDeflate.csproj +++ b/src/LibDeflate/LibDeflate.csproj @@ -10,7 +10,7 @@ libdeflate.net libdeflate deflate zlib gzip gz crc32 crc adler32 adler LibDeflate.NET is a managed wrapper around libdeflate, a native library for fast, whole-buffer DEFLATE-based compression and decompression. - latest + preview enable From 3724f4c98c537b129ed622fd362e36ec24850d83 Mon Sep 17 00:00:00 2001 From: "J. Zebedee" Date: Sun, 14 May 2023 19:30:45 -0500 Subject: [PATCH 30/44] 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 31/44] 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 32/44] 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 33/44] 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 34/44] 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 35/44] 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 36/44] 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 37/44] 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 38/44] 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 39/44] 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 40/44] 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 41/44] 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 42/44] 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 43/44] 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 44/44] 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