]> git.ipfire.org Git - thirdparty/zlib-ng.git/commitdiff
Add back zng_clz for big-endian and use macro for compare256
authorNathan Moinvaziri <nathan@nathanm.com>
Tue, 17 Mar 2026 06:35:05 +0000 (23:35 -0700)
committerHans Kristian Rosbach <hk-github@circlestorm.org>
Sun, 12 Apr 2026 20:36:15 +0000 (22:36 +0200)
arch/arm/compare256_neon.c
arch/generic/compare256_c.c
compare256_rle.h
fallback_builtins.h

index 4ced9fc9ca90cb7de27949f5a8091baad3071408..8a2c530f4b068eae1177accccafbc455334e2fac 100644 (file)
@@ -25,11 +25,11 @@ static inline uint32_t compare256_neon_static(const uint8_t *src0, const uint8_t
 
         lane = vgetq_lane_u64(vreinterpretq_u64_u8(cmp), 0);
         if (lane)
-            return len + zng_ctz64(lane) / 8;
+            return len + zng_first_diff_byte64(lane);
         len += 8;
         lane = vgetq_lane_u64(vreinterpretq_u64_u8(cmp), 1);
         if (lane)
-            return len + zng_ctz64(lane) / 8;
+            return len + zng_first_diff_byte64(lane);
         len += 8;
 
         src0 += 16, src1 += 16;
index 6934a555651bff96b63d823efe530c147478f014..a2b47751e5404ea8f86a129205d239feba4db616 100644 (file)
@@ -44,14 +44,14 @@ static inline uint32_t compare256_64_static(const uint8_t *src0, const uint8_t *
         uint64_t mv = zng_memread_8(src1);
         uint64_t diff = sv ^ mv;
         if (diff)
-            return len + zng_ctz64(Z_U64_TO_LE(diff)) / 8;
+            return len + zng_first_diff_byte64(diff);
         src0 += 8, src1 += 8, len += 8;
 
         sv = zng_memread_8(src0);
         mv = zng_memread_8(src1);
         diff = sv ^ mv;
         if (diff)
-            return len + zng_ctz64(Z_U64_TO_LE(diff)) / 8;
+            return len + zng_first_diff_byte64(diff);
         src0 += 8, src1 += 8, len += 8;
     } while (len < 256);
 
index 02e6bd496a3351ab892dab8925bd22a8271be325..37a9ab30da1ba72bfd74699f7fba0e336a489398 100644 (file)
@@ -52,7 +52,7 @@ static inline uint32_t compare256_rle_64(const uint8_t *src0, const uint8_t *src
         mv = zng_memread_8(src1);
         diff = sv ^ mv;
         if (diff)
-            return len + zng_ctz64(Z_U64_TO_LE(diff)) / 8;
+            return len + zng_first_diff_byte64(diff);
 
         src1 += 8, len += 8;
     } while (len < 256);
index e83b9752a57d475c572804bc337ee0d6940ceaa9..16ed1e70d58a59394f0a0bc7c22029da94342077 100644 (file)
@@ -67,6 +67,69 @@ Z_FORCEINLINE static uint32_t zng_ctz64(uint64_t value) {
 #endif
 }
 
+/* Count leading zeros (CLZ) functions with portable fallback.
+ *
+ * Predicate: Input must be non-zero. The result is undefined for zero input because
+ * __builtin_clz, BSR, and LZCNT all have undefined/different behavior for zero. LZCNT
+ * returns operand size for zero, BSR leaves destination undefined, and __builtin_clz
+ * is explicitly undefined per GCC/Clang docs. */
+
+Z_FORCEINLINE static uint32_t zng_clz32(uint32_t value) {
+    Assert(value != 0, "Invalid input value: 0");
+#if __has_builtin(__builtin_clz)
+    return (uint32_t)__builtin_clz(value);
+#elif defined(_MSC_VER) && !defined(__clang__)
+    unsigned long leading_zero;
+    _BitScanReverse(&leading_zero, value);
+    return 31u - (uint32_t)leading_zero;
+#else
+    /* Smear the highest set bit down, isolate it, then reuse de Bruijn CTZ */
+    value |= value >> 1;
+    value |= value >> 2;
+    value |= value >> 4;
+    value |= value >> 8;
+    value |= value >> 16;
+    return 31u - zng_ctz32((value >> 1) + 1u);
+#endif
+}
+
+Z_FORCEINLINE static uint32_t zng_clz64(uint64_t value) {
+    Assert(value != 0, "Invalid input value: 0");
+#if __has_builtin(__builtin_clzll)
+    return (uint32_t)__builtin_clzll(value);
+#elif defined(_MSC_VER) && !defined(__clang__) && defined(ARCH_64BIT)
+    unsigned long leading_zero;
+    _BitScanReverse64(&leading_zero, value);
+    return 63u - (uint32_t)leading_zero;
+#elif defined(_MSC_VER) && !defined(__clang__)
+    /* 32-bit MSVC fallback using two 32-bit scans */
+    unsigned long leading_zero;
+    if (_BitScanReverse(&leading_zero, (uint32_t)(value >> 32)))
+        return 31u - (uint32_t)leading_zero;
+    _BitScanReverse(&leading_zero, (uint32_t)value);
+    return 63u - (uint32_t)leading_zero;
+#else
+    /* Smear the highest set bit down, isolate it, then reuse de Bruijn CTZ */
+    value |= value >> 1;
+    value |= value >> 2;
+    value |= value >> 4;
+    value |= value >> 8;
+    value |= value >> 16;
+    value |= value >> 32;
+    return 63u - zng_ctz64((value >> 1) + 1ull);
+#endif
+}
+
+/* Byte-position of the first differing byte in a native-endian XOR diff,
+ * using CTZ on little-endian and CLZ on big-endian to avoid a byte-swap. */
+#if BYTE_ORDER == BIG_ENDIAN
+#  define zng_first_diff_byte32(diff) (zng_clz32(diff) / 8)
+#  define zng_first_diff_byte64(diff) (zng_clz64(diff) / 8)
+#else
+#  define zng_first_diff_byte32(diff) (zng_ctz32(diff) / 8)
+#  define zng_first_diff_byte64(diff) (zng_ctz64(diff) / 8)
+#endif
+
 Z_FORCEINLINE static uint16_t zng_bitreverse16(uint16_t value) {
 #if __has_builtin(__builtin_bitreverse16)
     return (uint16_t)__builtin_bitreverse16(value);