Merge pull request #234 from anthony-zy/rv_extractoffset

RISC-V: Optimize decompression with branchless AdvanceToNextTagRVOptimized +13.7%
diff --git a/snappy-internal.h b/snappy-internal.h
index 00b2db5..9be0542 100644
--- a/snappy-internal.h
+++ b/snappy-internal.h
@@ -194,7 +194,7 @@
 // riscv and little-endian cpu choose this routinue can be done faster too.
 #if !SNAPPY_IS_BIG_ENDIAN && \
     (defined(__x86_64__) || defined(_M_X64) || defined(ARCH_PPC) || \
-     defined(ARCH_ARM) || defined(__riscv))
+     defined(ARCH_ARM) || (defined(__riscv) && (__riscv_xlen == 64)))
 static inline std::pair<size_t, bool> FindMatchLength(const char* s1,
                                                       const char* s2,
                                                       const char* s2_limit,
diff --git a/snappy.cc b/snappy.cc
index 8be5fa4..f62db8d 100644
--- a/snappy.cc
+++ b/snappy.cc
@@ -1401,7 +1401,13 @@
          reinterpret_cast<const char*>(&kExtractMasksCombined) + 2 * tag_type,
          sizeof(result));
   return val & result;
-#elif defined(__aarch64__)
+  // For AArch64 and RISC-V, use a bit-twiddling trick to extract the mask from a
+  // single combined constant instead of a lookup table. The constant packs multiple
+  // 16-bit masks based on tag_type (see implementation below). The code calculates
+  // the shift amount from tag_type, right-shifts the constant to move the desired
+  // mask to the LSB position, then extracts it with & 0xFFFF. This branchless
+  // approach is often more performant on modern CPUs.
+#elif defined(__aarch64__) || (defined(__riscv) && (__riscv_xlen == 64))
   constexpr uint64_t kExtractMasksCombined = 0x0000FFFF00FF0000ull;
   return val & static_cast<uint32_t>(
       (kExtractMasksCombined >> (tag_type * 16)) & 0xFFFF);