From 75a898342c4964cffeaf803da296112fb1f432da Mon Sep 17 00:00:00 2001 From: Aristarkh Zagorodnikov Date: Sat, 20 Sep 2025 21:51:46 +0100 Subject: [PATCH 1/2] build: add netstandard2.0 build support --- .../CardinalityEstimation.csproj | 3 ++- CardinalityEstimation/CardinalityEstimator.cs | 11 ++++---- .../ConcurrentCardinalityEstimator.cs | 10 +++---- .../Polyfills/BitOperations.cs | 26 +++++++++++++++++++ CardinalityEstimation/Polyfills/Linq.cs | 13 ++++++++++ 5 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 CardinalityEstimation/Polyfills/BitOperations.cs create mode 100644 CardinalityEstimation/Polyfills/Linq.cs diff --git a/CardinalityEstimation/CardinalityEstimation.csproj b/CardinalityEstimation/CardinalityEstimation.csproj index 1870c90..55724b3 100644 --- a/CardinalityEstimation/CardinalityEstimation.csproj +++ b/CardinalityEstimation/CardinalityEstimation.csproj @@ -1,6 +1,7 @@  - net9.0;net8.0 + net9.0;net8.0;netstandard2.0 + latest Debug;Release;Release-Signed true CardinalityEstimation.Signed diff --git a/CardinalityEstimation/CardinalityEstimator.cs b/CardinalityEstimation/CardinalityEstimator.cs index ae526c1..08aa788 100644 --- a/CardinalityEstimation/CardinalityEstimator.cs +++ b/CardinalityEstimation/CardinalityEstimator.cs @@ -27,6 +27,7 @@ namespace CardinalityEstimation { using System; using System.Collections.Generic; + using System.Numerics; using System.Text; using Hash; @@ -214,8 +215,8 @@ internal CardinalityEstimator(GetHashCodeDelegate hashFunction, CardinalityEstim this.hashFunction = hashFunction; if (this.hashFunction == null) { - this.hashFunction = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x)); - this.hashFunctionSpan = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x)); + this.hashFunction = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x), 0); + this.hashFunctionSpan = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x), 0); } else { @@ -237,8 +238,8 @@ internal CardinalityEstimator(GetHashCodeSpanDelegate hashFunctionSpan, Cardinal this.hashFunctionSpan = hashFunctionSpan; if (this.hashFunctionSpan == null) { - this.hashFunction = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x)); - this.hashFunctionSpan = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x)); + this.hashFunction = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x), 0); + this.hashFunctionSpan = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x), 0); } else { @@ -846,7 +847,7 @@ public static byte GetSigma(ulong hash, byte bitsToCount) int knownZeros = 64 - bitsToCount; var masked = hash & mask; - var leadingZeros = (byte)ulong.LeadingZeroCount(masked); + var leadingZeros = (byte)BitOperations.LeadingZeroCount(masked); return (byte)(leadingZeros - knownZeros + 1); } diff --git a/CardinalityEstimation/ConcurrentCardinalityEstimator.cs b/CardinalityEstimation/ConcurrentCardinalityEstimator.cs index cb8e985..0893d9a 100644 --- a/CardinalityEstimation/ConcurrentCardinalityEstimator.cs +++ b/CardinalityEstimation/ConcurrentCardinalityEstimator.cs @@ -168,7 +168,7 @@ public ConcurrentCardinalityEstimator(CardinalityEstimator other) lockSlim = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); // Init the hash function - use default since we can't get it from the other estimator - hashFunction = ((x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x))); + hashFunction = ((x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x), 0)); sparseMaxElements = Math.Max(0, (m / 15) - 10); @@ -217,8 +217,8 @@ internal ConcurrentCardinalityEstimator(GetHashCodeDelegate hashFunction, Cardin this.hashFunction = hashFunction; if (this.hashFunction == null) { - this.hashFunction = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x)); - this.hashFunctionSpan = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x)); + this.hashFunction = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x), 0); + this.hashFunctionSpan = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x), 0); } else { @@ -237,8 +237,8 @@ internal ConcurrentCardinalityEstimator(GetHashCodeSpanDelegate hashFunctionSpan this.hashFunctionSpan = hashFunctionSpan; if (this.hashFunction == null) { - this.hashFunction = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x)); - this.hashFunctionSpan = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x)); + this.hashFunction = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x), 0); + this.hashFunctionSpan = (x) => BitConverter.ToUInt64(System.IO.Hashing.XxHash128.Hash(x), 0); } } diff --git a/CardinalityEstimation/Polyfills/BitOperations.cs b/CardinalityEstimation/Polyfills/BitOperations.cs new file mode 100644 index 0000000..ea26037 --- /dev/null +++ b/CardinalityEstimation/Polyfills/BitOperations.cs @@ -0,0 +1,26 @@ +#if !NETCOREAPP3_0_OR_GREATER +namespace System.Numerics; + +public static class BitOperations +{ + public static uint LeadingZeroCount(ulong x) + { + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x |= x >> 32; + + x -= x >> 1 & 0x5555555555555555; + x = (x >> 2 & 0x3333333333333333) + (x & 0x3333333333333333); + x = (x >> 4) + x & 0x0f0f0f0f0f0f0f0f; + x += x >> 8; + x += x >> 16; + x += x >> 32; + + const int numLongBits = sizeof(long) * 8; + return numLongBits - (uint)(x & 0x0000007f); + } +} +#endif diff --git a/CardinalityEstimation/Polyfills/Linq.cs b/CardinalityEstimation/Polyfills/Linq.cs new file mode 100644 index 0000000..b76ca3e --- /dev/null +++ b/CardinalityEstimation/Polyfills/Linq.cs @@ -0,0 +1,13 @@ +#if !NETCOREAPP2_1_OR_GREATER +using System.Collections.Generic; + +namespace System.Linq; + +public static class LinqExtensions +{ + public static HashSet ToHashSet(this IEnumerable source) + { + return new(source); + } +} +#endif \ No newline at end of file From 568b7f140dcf42c460e38a91036840be022930e1 Mon Sep 17 00:00:00 2001 From: Aristarkh Zagorodnikov Date: Tue, 21 Oct 2025 21:27:07 +0100 Subject: [PATCH 2/2] fix: internalize polyfills to prevent pollution --- CardinalityEstimation/Polyfills/BitOperations.cs | 2 +- CardinalityEstimation/Polyfills/Linq.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CardinalityEstimation/Polyfills/BitOperations.cs b/CardinalityEstimation/Polyfills/BitOperations.cs index ea26037..cb84892 100644 --- a/CardinalityEstimation/Polyfills/BitOperations.cs +++ b/CardinalityEstimation/Polyfills/BitOperations.cs @@ -1,7 +1,7 @@ #if !NETCOREAPP3_0_OR_GREATER namespace System.Numerics; -public static class BitOperations +internal static class BitOperations { public static uint LeadingZeroCount(ulong x) { diff --git a/CardinalityEstimation/Polyfills/Linq.cs b/CardinalityEstimation/Polyfills/Linq.cs index b76ca3e..940a7f2 100644 --- a/CardinalityEstimation/Polyfills/Linq.cs +++ b/CardinalityEstimation/Polyfills/Linq.cs @@ -3,7 +3,7 @@ namespace System.Linq; -public static class LinqExtensions +internal static class LinqExtensions { public static HashSet ToHashSet(this IEnumerable source) {