diff --git a/CMakeLists.txt b/CMakeLists.txt
index cd71a47..a070808 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -89,11 +89,11 @@ endif(MSVC)
 
 # BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to make
 # it prominent in the GUI.
-option(BUILD_SHARED_LIBS "Build shared libraries(DLLs)." OFF)
+option(BUILD_SHARED_LIBS "Build shared libraries(DLLs)." ON)
 
-option(SNAPPY_BUILD_TESTS "Build Snappy's own tests." ON)
+option(SNAPPY_BUILD_TESTS "Build Snappy's own tests." OFF)
 
-option(SNAPPY_BUILD_BENCHMARKS "Build Snappy's benchmarks" ON)
+option(SNAPPY_BUILD_BENCHMARKS "Build Snappy's benchmarks" OFF)
 
 option(SNAPPY_FUZZING_BUILD "Build Snappy for fuzzing." OFF)
 
diff --git a/snappy-sinksource.cc b/snappy-sinksource.cc
index 8214964..1b0f50f 100644
--- a/snappy-sinksource.cc
+++ b/snappy-sinksource.cc
@@ -67,12 +67,12 @@ ByteArraySource::~ByteArraySource() = default;
 
 size_t ByteArraySource::Available() const { return left_; }
 
-const char* ByteArraySource::Peek(size_t* len) {
+inline const char* ByteArraySource::Peek(size_t* len) {
   *len = left_;
   return ptr_;
 }
 
-void ByteArraySource::Skip(size_t n) {
+inline void ByteArraySource::Skip(size_t n) {
   left_ -= n;
   ptr_ += n;
 }
diff --git a/snappy.cc b/snappy.cc
index 8dc3713..a1ea452 100644
--- a/snappy.cc
+++ b/snappy.cc
@@ -79,6 +79,9 @@
 #include <string>
 #include <utility>
 #include <vector>
+#ifdef __aarch64__
+  #include <arm_neon.h>
+#endif
 
 namespace snappy {
 
@@ -102,6 +105,57 @@ using internal::V128_StoreU;
 using internal::V128_DupChar;
 #endif
 
+// GCC dispatches to libc for memmoves > 16 bytes, so we need to
+// do some work to get good code from that compiler. Clang handles
+// powers-of-2 at least up to 64 well.
+#if !defined(__GNUC__) || defined(__clang__)
+  template <size_t SIZE>
+  SNAPPY_ATTRIBUTE_ALWAYS_INLINE
+  inline void FixedSizeMemMove(void* dest, const void* src) {
+    memmove(dest, src, SIZE);
+  }
+#else
+  template <size_t SIZE>
+  SNAPPY_ATTRIBUTE_ALWAYS_INLINE
+  inline void FixedSizeMemMove(void* dest, const void* src) {
+    if (SIZE <= 16) {
+      // gcc has patterns for memmove up to 16 bytes
+      memmove(dest, src, SIZE);
+    } else {
+      // This generates reasonable code on x86_64, but on aarch64 this produces a
+      // dead store to tmp, plus takes up stack space.
+      char tmp[SIZE];
+      memcpy(tmp, src, SIZE);
+      memcpy(dest, tmp, SIZE);
+    }
+  }
+
+  #ifdef __aarch64__ // Implies neon support
+    template <>
+    SNAPPY_ATTRIBUTE_ALWAYS_INLINE
+    inline void FixedSizeMemMove<32>(void* dest, const void* src) {
+      V128 a = V128_LoadU(reinterpret_cast<const V128*>(src));
+      V128 b = V128_LoadU(reinterpret_cast<const V128*>(src) + 1);
+      V128_StoreU(reinterpret_cast<V128*>(dest), a);
+      V128_StoreU(reinterpret_cast<V128*>(dest) + 1, b);
+    }
+
+    template <>
+    SNAPPY_ATTRIBUTE_ALWAYS_INLINE
+    inline void FixedSizeMemMove<64>(void* dest, const void* src) {
+      V128 a = V128_LoadU(reinterpret_cast<const V128*>(src));
+      V128 b = V128_LoadU(reinterpret_cast<const V128*>(src) + 1);
+      V128 c = V128_LoadU(reinterpret_cast<const V128*>(src) + 2);
+      V128 d = V128_LoadU(reinterpret_cast<const V128*>(src) + 3);
+      V128_StoreU(reinterpret_cast<V128*>(dest), a);
+      V128_StoreU(reinterpret_cast<V128*>(dest) + 1, b);
+      V128_StoreU(reinterpret_cast<V128*>(dest) + 2, c);
+      V128_StoreU(reinterpret_cast<V128*>(dest) + 3, d);
+    }
+  #endif
+#endif
+  
+
 // We translate the information encoded in a tag through a lookup table to a
 // format that requires fewer instructions to decode. Effectively we store
 // the length minus the tag part of the offset. The lowest significant byte
@@ -152,6 +206,7 @@ alignas(64) const std::array<int16_t, 256> kLengthMinusOffset =
 // better compression for compressible input.
 //
 // REQUIRES: mask is 2 * (table_size - 1), and table_size is a power of two.
+SNAPPY_ATTRIBUTE_ALWAYS_INLINE
 inline uint16_t* TableEntry(uint16_t* table, uint32_t bytes, uint32_t mask) {
   // Our choice is quicker-and-dirtier than the typical hash function;
   // empirically, that seems beneficial.  The upper bits of kMagic * bytes are a
@@ -226,7 +281,7 @@ void UnalignedCopy64(const void* src, void* dst) {
   std::memcpy(dst, tmp, 8);
 }
 
-void UnalignedCopy128(const void* src, void* dst) {
+inline void UnalignedCopy128(const void* src, void* dst) {
   // std::memcpy() gets vectorized when the appropriate compiler options are
   // used. For example, x86 compilers targeting SSE2+ will optimize to an SSE2
   // load and store.
@@ -679,7 +734,20 @@ static inline char* EmitCopyAtMost64(char* op, size_t offset, size_t len) {
     // benchmarks. This code produces branch free code, the data dependency
     // chain that bottlenecks the throughput is so long that a few extra
     // instructions are completely free (IPC << 6 because of data deps).
+
+#if defined(__GNUC__) && defined(__aarch64__)  // ARM64 架构判定
+    uint32_t temp;
+    __asm__ __volatile__(
+      "cmp %w1, #2048\n\t"
+      "csel %w0, %w2, %w3, lo\n\t"
+      : "=&r"(temp)
+      : "r"(offset), "r"(copy1), "r"(copy2)
+      : "cc"
+    );
+    u += temp;
+#else
     u += offset < 2048 ? copy1 : copy2;
+#endif
     LittleEndian::Store32(op, u);
     op += offset < 2048 ? 2 : 3;
   } else {
@@ -832,20 +900,28 @@ char* CompressFragment(const char* input, size_t input_size, char* op,
       // The "skip" variable keeps track of how many bytes there are since the
       // last match; dividing it by 32 (ie. right-shifting by five) gives the
       // number of bytes to move ahead for each iteration.
-      uint32_t skip = 32;
+      uint32_t skip = 64;
 
       const char* candidate;
       if (ip_limit - ip >= 16) {
         auto delta = ip - base_ip;
+        uint32_t next_dword = preload;
+        uint16_t* next_table_entry = TableEntry(table, next_dword, mask);
         for (int j = 0; j < 4; ++j) {
           for (int k = 0; k < 4; ++k) {
+            if (k == 3) {
+              data = LittleEndian::Load64(ip + 4 * j + 4);
+            } else {
+              data >>= 8;
+            }
             int i = 4 * j + k;
             // These for-loops are meant to be unrolled. So we can freely
             // special case the first iteration to use the value already
             // loaded in preload.
-            uint32_t dword = i == 0 ? preload : static_cast<uint32_t>(data);
-            assert(dword == LittleEndian::Load32(ip + i));
-            uint16_t* table_entry = TableEntry(table, dword, mask);
+            uint32_t dword = next_dword;
+            next_dword = static_cast<uint32_t>(data);
+            uint16_t* table_entry = next_table_entry;
+            next_table_entry = TableEntry(table, next_dword, mask);
             candidate = base_ip + *table_entry;
             assert(candidate >= base_ip);
             assert(candidate < ip + i);
@@ -855,11 +931,10 @@ char* CompressFragment(const char* input, size_t input_size, char* op,
               UnalignedCopy128(next_emit, op + 1);
               ip += i;
               op = op + i + 2;
+              *next_table_entry = delta + i + 1;
               goto emit_match;
             }
-            data >>= 8;
           }
-          data = LittleEndian::Load64(ip + 4 * j + 4);
         }
         ip += 16;
         skip += 16;
@@ -875,6 +950,7 @@ char* CompressFragment(const char* input, size_t input_size, char* op,
           goto emit_remainder;
         }
         candidate = base_ip + *table_entry;
+        SNAPPY_PREFETCH(candidate);
         assert(candidate >= base_ip);
         assert(candidate < ip);
 
@@ -908,10 +984,13 @@ char* CompressFragment(const char* input, size_t input_size, char* op,
         const char* base = ip;
         std::pair<size_t, bool> p =
             FindMatchLength(candidate + 4, ip + 4, ip_end, &data);
+        uint16_t* table_entry = TableEntry(table, data, mask);
         size_t matched = 4 + p.first;
         ip += matched;
         size_t offset = base - candidate;
         assert(0 == memcmp(base, candidate, matched));
+        candidate = base_ip + *table_entry;
+        SNAPPY_PREFETCH(candidate);
         if (p.second) {
           op = EmitCopy</*len_less_than_12=*/true>(op, offset, matched);
         } else {
@@ -926,10 +1005,13 @@ char* CompressFragment(const char* input, size_t input_size, char* op,
         // We are now looking for a 4-byte match again.  We read
         // table[Hash(ip, mask)] for that.  To improve compression,
         // we also update table[Hash(ip - 1, mask)] and table[Hash(ip, mask)].
+        
         *TableEntry(table, LittleEndian::Load32(ip - 1), mask) =
             ip - base_ip - 1;
-        uint16_t* table_entry = TableEntry(table, data, mask);
-        candidate = base_ip + *table_entry;
+        *TableEntry(table, LittleEndian::Load32(ip - 2), mask) =
+            ip - base_ip - 2;
+        //uint16_t* table_entry = TableEntry(table, data, mask);
+        //candidate = base_ip + *table_entry;
         *table_entry = ip - base_ip;
         // Measurements on the benchmarks have shown the following probabilities
         // for the loop to exit (ie. avg. number of iterations is reciprocal).
@@ -1246,6 +1328,11 @@ void MemCopy64(char* dst, const void* src, size_t size) {
     data = _mm256_lddqu_si256(static_cast<const __m256i *>(src) + 1);
     _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst) + 1, data);
   }
+#elif defined(__aarch64__)
+    // Emperically it is faster to just copy all 64 rather than branching.
+    (void)kShortMemCopy;
+    (void)size;
+    FixedSizeMemMove<64>(dst, src);
 #else
   std::memmove(dst, src, kShortMemCopy);
   // Profiling shows that nearly all copies are short.
@@ -1257,20 +1344,20 @@ void MemCopy64(char* dst, const void* src, size_t size) {
 #endif
 }
 
-void MemCopy64(ptrdiff_t dst, const void* src, size_t size) {
+inline void MemCopy64(ptrdiff_t dst, const void* src, size_t size) {
   // TODO: Switch to [[maybe_unused]] when we can assume C++17.
   (void)dst;
   (void)src;
   (void)size;
 }
 
-void ClearDeferred(const void** deferred_src, size_t* deferred_length,
+inline void ClearDeferred(const void** deferred_src, size_t* deferred_length,
                    uint8_t* safe_source) {
   *deferred_src = safe_source;
   *deferred_length = 0;
 }
 
-void DeferMemCopy(const void** deferred_src, size_t* deferred_length,
+inline void DeferMemCopy(const void** deferred_src, size_t* deferred_length,
                   const void* src, size_t length) {
   *deferred_src = src;
   *deferred_length = length;
@@ -1279,23 +1366,11 @@ void DeferMemCopy(const void** deferred_src, size_t* deferred_length,
 SNAPPY_ATTRIBUTE_ALWAYS_INLINE
 inline size_t AdvanceToNextTagARMOptimized(const uint8_t** ip_p, size_t* tag) {
   const uint8_t*& ip = *ip_p;
-  // This section is crucial for the throughput of the decompression loop.
-  // The latency of an iteration is fundamentally constrained by the
-  // following data chain on ip.
-  // ip -> c = Load(ip) -> delta1 = (c & 3)        -> ip += delta1 or delta2
-  //                       delta2 = ((c >> 2) + 1)    ip++
-  // This is different from X86 optimizations because ARM has conditional add
-  // instruction (csinc) and it removes several register moves.
-  const size_t tag_type = *tag & 3;
-  const bool is_literal = (tag_type == 0);
-  if (is_literal) {
-    size_t next_literal_tag = (*tag >> 2) + 1;
-    *tag = ip[next_literal_tag];
-    ip += next_literal_tag + 1;
-  } else {
-    *tag = ip[tag_type];
-    ip += tag_type + 1;
-  }
+  const size_t current_tag = *tag;
+  const size_t tag_type = current_tag & 3;
+  const size_t offset = (tag_type == 0) ? ((current_tag >> 2) + 1) : tag_type;
+  *tag = ip[offset];
+  ip += offset + 1;
   return tag_type;
 }
 
@@ -1510,7 +1585,7 @@ class SnappyDecompressor {
   // Returns true on success, false on error or end of input.
   bool RefillTag();
 
-  void ResetLimit(const char* ip) {
+  inline void ResetLimit(const char* ip) {
     ip_limit_min_maxtaglen_ =
         ip_limit_ - std::min<ptrdiff_t>(ip_limit_ - ip, kMaximumTagLength - 1);
   }
@@ -1681,7 +1756,7 @@ class SnappyDecompressor {
   }
 };
 
-constexpr uint32_t CalculateNeeded(uint8_t tag) {
+inline constexpr uint32_t CalculateNeeded(uint8_t tag) {
   return ((tag & 3) == 0 && tag >= (60 * 4))
              ? (tag >> 2) - 58
              : (0x05030201 >> ((tag * 8) & 31)) & 0xFF;
@@ -1701,7 +1776,7 @@ constexpr bool VerifyCalculateNeeded() {
 static_assert(VerifyCalculateNeeded(), "");
 #endif  // c++14
 
-bool SnappyDecompressor::RefillTag() {
+inline bool SnappyDecompressor::RefillTag() {
   const char* ip = ip_;
   if (ip == ip_limit_) {
     // Fetch a new fragment from the reader
@@ -2213,7 +2288,11 @@ class SnappyArrayWriter {
       *op_p = IncrementalCopy(op - offset, op, op_end, op_limit_);
       return true;
     }
+#ifdef __aarch64__
+    FixedSizeMemMove<kSlopBytes>(op, op - offset);
+#else
     std::memmove(op, op - offset, kSlopBytes);
+#endif
     *op_p = op_end;
     return true;
   }
@@ -2488,7 +2567,11 @@ class SnappyScatteredWriter {
     }
     // Fast path
     char* const op_end = op + len;
+#ifdef __aarch64__
+    FixedSizeMemMove<kSlopBytes>(op, op - offset);
+#else
     std::memmove(op, op - offset, kSlopBytes);
+#endif
     *op_p = op_end;
     return true;
   }
@@ -2646,3 +2729,4 @@ bool Uncompress(Source* compressed, Sink* uncompressed) {
 }
 
 }  // namespace snappy
+
diff --git a/snappy.h b/snappy.h
index 2f1b802..d3d540f 100644
--- a/snappy.h
+++ b/snappy.h
@@ -250,7 +250,7 @@ namespace snappy {
   static constexpr int kMinHashTableBits = 8;
   static constexpr size_t kMinHashTableSize = 1 << kMinHashTableBits;
 
-  static constexpr int kMaxHashTableBits = 15;
+  static constexpr int kMaxHashTableBits = 14;
   static constexpr size_t kMaxHashTableSize = 1 << kMaxHashTableBits;
 }  // end namespace snappy