diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,4 @@
+0.2.0
+---
+
+- Fix MacOS, Windows Build
diff --git a/bench/Bench.hs b/bench/Bench.hs
new file mode 100644
--- /dev/null
+++ b/bench/Bench.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE BangPatterns #-}
+module Main where
+
+import qualified Data.ByteString as BS
+import           Data.ByteString (ByteString)
+import           Data.Word
+import           Criterion.Main
+
+import           Data.Digest.CRC32C
+
+main =
+  defaultMain [
+    env (pure (largeByteString (2^8) 42)) $ \bs ->
+    bench "crc32c small (2^8)" $ whnf crc32c bs
+
+  , env (pure (largeByteString (2^16) 42)) $ \bs ->
+    bench "crc32c large (2^16)" $ whnf crc32c bs
+
+  , env (pure (largeByteString (2^16+1) 42)) $ \bs ->
+    bench "crc32c large (2^16+1)" $ whnf crc32c bs
+  ]
+
+largeByteString :: Int -> Word -> ByteString
+largeByteString n seed =
+    fst $ BS.unfoldrN n (Just . step) seed
+  where
+    -- C11 example linear congruential generator
+    step :: Word -> (Word8, Word)
+    step rng = (w8, rng')
+      where
+        !rng' = rng * 1103515245 + 12345
+        !w8   = fromIntegral (rng' `div` 65536)
+
diff --git a/crc32c.cabal b/crc32c.cabal
--- a/crc32c.cabal
+++ b/crc32c.cabal
@@ -1,27 +1,23 @@
-cabal-version: 1.12
-
--- This file has been generated from package.yaml by hpack version 0.35.1.
---
--- see: https://github.com/sol/hpack
---
--- hash: 1a4d90d4e9381a7e6ebad0ead5753fb5d408d10dca3875cc575fa3b9a83becbc
+cabal-version: 2.2
 
 name:           crc32c
-version:        0.1.0
-synopsis:       Haskell bindings for crc32c
+version:        0.2.0
+description:    Haskell bindings for crc32c
+synopsis:       crc32c
 category:       FFI, Raw
 homepage:       https://github.com/leptonyu/crc32c#readme
 author:         Daniel YU
 maintainer:     leptonyu@gmail.com
 copyright:      (c) Daniel YU
-license:        BSD3
+license:        BSD-3-Clause
 license-file:   LICENSE
 build-type:     Simple
 extra-source-files:
     README.md
+    ChangeLog.md
     include/LICENSE
     include/crc32c_arm64.h
-    include/crc32c_arm64_linux_check.h
+    include/crc32c_arm64_check.h
     include/crc32c_internal.h
     include/crc32c_prefetch.h
     include/crc32c_read_le.h
@@ -34,45 +30,49 @@
 library
   exposed-modules:
       Data.Digest.CRC32C
-  other-modules:
-      Data.Digest.CRC32C.Internal
   hs-source-dirs:
       src
+  build-depends:
+      base >=4.9 && <5
+    , bytestring >=0.11.5
+  default-language: Haskell2010
+
   include-dirs:
       include
-  c-sources:
+  cxx-sources:
       include/crc32c.cc
       include/crc32c_arm64.cc
       include/crc32c_portable.cc
       include/crc32c_sse42.cc
   extra-libraries:
       stdc++
-  build-tools:
-      c2hs
-  build-depends:
-      base >=4.9 && <5
-    , bytestring >=0.10.8.2
-  default-language: Haskell2010
-  if arch(x86_64)
-    cc-options: -std=c++11 -D__HAVE_SSE42=1 -D__HAVE_ARM64_CRC32C=0 -msse4.2
-  else
-    if arch(arm)
-      cc-options: -std=c++11 -D__HAVE_SSE42=0 -D__HAVE_ARM64_CRC32C=1
-    else
-      cc-options: -std=c++11 -D__HAVE_SSE42=0 -D__HAVE_ARM64_CRC32C=0
+  cxx-options: -std=c++11
+  if (arch(x86_64) && !os(windows))
+    cxx-options: -DHAVE_SSE42=1 -msse4.2
+  if arch(arm)
+    cxx-options: -DHAVE_ARM64_CRC32C=1
 
 test-suite crc32c-test
   type: exitcode-stdio-1.0
   main-is: Spec.hs
-  other-modules:
-      Paths_crc32c
   hs-source-dirs:
       test
   build-depends:
       QuickCheck
-    , base >=4.9 && <5
-    , bytestring >=0.10.8.2 && <0.11
+    , base
+    , bytestring
     , crc32c
     , hspec
     , hspec-core
+  default-language: Haskell2010
+
+benchmark crc32c-bench
+  type: exitcode-stdio-1.0
+  main-is: Bench.hs
+  hs-source-dirs: bench
+  build-depends:
+      base
+    , bytestring
+    , crc32c
+    , criterion
   default-language: Haskell2010
diff --git a/include/crc32c.cc b/include/crc32c.cc
--- a/include/crc32c.cc
+++ b/include/crc32c.cc
@@ -8,7 +8,7 @@
 #include <cstdint>
 
 #include "./crc32c_arm64.h"
-#include "./crc32c_arm64_linux_check.h"
+#include "./crc32c_arm64_check.h"
 #include "./crc32c_internal.h"
 #include "./crc32c_sse42.h"
 #include "./crc32c_sse42_check.h"
@@ -20,8 +20,8 @@
   static bool can_use_sse42 = CanUseSse42();
   if (can_use_sse42) return ExtendSse42(crc, data, count);
 #elif HAVE_ARM64_CRC32C
-  static bool can_use_arm_linux = CanUseArm64Linux();
-  if (can_use_arm_linux) return ExtendArm64(crc, data, count);
+  static bool can_use_arm64_crc32 = CanUseArm64Crc32();
+  if (can_use_arm64_crc32) return ExtendArm64(crc, data, count);
 #endif  // HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
 
   return ExtendPortable(crc, data, count);
diff --git a/include/crc32c_arm64.cc b/include/crc32c_arm64.cc
--- a/include/crc32c_arm64.cc
+++ b/include/crc32c_arm64.cc
@@ -62,7 +62,7 @@
 
 namespace crc32c {
 
-uint32_t ExtendArm64(uint32_t crc, const uint8_t *buf, size_t size) {
+uint32_t ExtendArm64(uint32_t crc, const uint8_t *data, size_t size) {
   int64_t length = size;
   uint32_t crc0, crc1, crc2, crc3;
   uint64_t t0, t1, t2;
@@ -72,7 +72,6 @@
   const poly64_t k0 = 0x8d96551c, k1 = 0xbd6f81f8, k2 = 0xdcb17aa4;
 
   crc = crc ^ kCRC32Xor;
-  const uint8_t *p = reinterpret_cast<const uint8_t *>(buf);
 
   while (length >= KBYTES) {
     crc0 = crc;
@@ -81,14 +80,14 @@
     crc3 = 0;
 
     // Process 1024 bytes in parallel.
-    CRC32C1024BYTES(p);
+    CRC32C1024BYTES(data);
 
     // Merge the 4 partial CRC32C values.
     t2 = (uint64_t)vmull_p64(crc2, k2);
     t1 = (uint64_t)vmull_p64(crc1, k1);
     t0 = (uint64_t)vmull_p64(crc0, k0);
-    crc = __crc32cd(crc3, *(uint64_t *)p);
-    p += sizeof(uint64_t);
+    crc = __crc32cd(crc3, *(uint64_t *)data);
+    data += sizeof(uint64_t);
     crc ^= __crc32cd(0, t2);
     crc ^= __crc32cd(0, t1);
     crc ^= __crc32cd(0, t0);
@@ -97,23 +96,23 @@
   }
 
   while (length >= 8) {
-    crc = __crc32cd(crc, *(uint64_t *)p);
-    p += 8;
+    crc = __crc32cd(crc, *(uint64_t *)data);
+    data += 8;
     length -= 8;
   }
 
   if (length & 4) {
-    crc = __crc32cw(crc, *(uint32_t *)p);
-    p += 4;
+    crc = __crc32cw(crc, *(uint32_t *)data);
+    data += 4;
   }
 
   if (length & 2) {
-    crc = __crc32ch(crc, *(uint16_t *)p);
-    p += 2;
+    crc = __crc32ch(crc, *(uint16_t *)data);
+    data += 2;
   }
 
   if (length & 1) {
-    crc = __crc32cb(crc, *p);
+    crc = __crc32cb(crc, *data);
   }
 
   return crc ^ kCRC32Xor;
diff --git a/include/crc32c_arm64.h b/include/crc32c_arm64.h
--- a/include/crc32c_arm64.h
+++ b/include/crc32c_arm64.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file. See the AUTHORS file for names of contributors.
 
-// Linux-specific code checking the availability for ARM CRC32C instructions.
+// ARM-specific code
 
-#ifndef CRC32C_CRC32C_ARM_LINUX_H_
-#define CRC32C_CRC32C_ARM_LINUX_H_
+#ifndef CRC32C_CRC32C_ARM_H_
+#define CRC32C_CRC32C_ARM_H_
 
 #include <cstddef>
 #include <cstdint>
@@ -22,4 +22,4 @@
 
 #endif  // HAVE_ARM64_CRC32C
 
-#endif  // CRC32C_CRC32C_ARM_LINUX_H_
+#endif  // CRC32C_CRC32C_ARM_H_
diff --git a/include/crc32c_arm64_check.h b/include/crc32c_arm64_check.h
new file mode 100644
--- /dev/null
+++ b/include/crc32c_arm64_check.h
@@ -0,0 +1,66 @@
+// Copyright 2017 The CRC32C Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+// ARM-specific code checking for the availability of CRC32C instructions.
+
+#ifndef CRC32C_CRC32C_ARM_CHECK_H_
+#define CRC32C_CRC32C_ARM_CHECK_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "crc32c/crc32c_config.h"
+
+#if HAVE_ARM64_CRC32C
+
+#ifdef __linux__
+#if HAVE_STRONG_GETAUXVAL
+#include <sys/auxv.h>
+#elif HAVE_WEAK_GETAUXVAL
+// getauxval() is not available on Android until API level 20. Link it as a weak
+// symbol.
+extern "C" unsigned long getauxval(unsigned long type) __attribute__((weak));
+
+#define AT_HWCAP 16
+#endif  // HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL
+#endif  // defined (__linux__)
+
+#ifdef __APPLE__
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#endif  // defined (__APPLE__)
+
+namespace crc32c {
+
+inline bool CanUseArm64Crc32() {
+#if defined (__linux__) && (HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL)
+  // From 'arch/arm64/include/uapi/asm/hwcap.h' in Linux kernel source code.
+  constexpr unsigned long kHWCAP_PMULL = 1 << 4;
+  constexpr unsigned long kHWCAP_CRC32 = 1 << 7;
+  unsigned long hwcap =
+#if HAVE_STRONG_GETAUXVAL
+      // Some compilers warn on (&getauxval != nullptr) in the block below.
+      getauxval(AT_HWCAP);
+#elif HAVE_WEAK_GETAUXVAL
+      (&getauxval != nullptr) ? getauxval(AT_HWCAP) : 0;
+#else
+#error This is supposed to be nested inside a check for HAVE_*_GETAUXVAL.
+#endif  // HAVE_STRONG_GETAUXVAL
+  return (hwcap & (kHWCAP_PMULL | kHWCAP_CRC32)) ==
+         (kHWCAP_PMULL | kHWCAP_CRC32);
+#elif defined(__APPLE__)
+  int val = 0;
+  size_t len = sizeof(val);
+  return sysctlbyname("hw.optional.armv8_crc32", &val, &len, nullptr, 0) == 0
+             && val != 0;
+#else
+  return false;
+#endif  // HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL
+}
+
+}  // namespace crc32c
+
+#endif  // HAVE_ARM64_CRC32C
+
+#endif  // CRC32C_CRC32C_ARM_CHECK_H_
diff --git a/include/crc32c_arm64_linux_check.h b/include/crc32c_arm64_linux_check.h
deleted file mode 100644
--- a/include/crc32c_arm64_linux_check.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 The CRC32C Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file. See the AUTHORS file for names of contributors.
-
-// ARM Linux-specific code checking for the availability of CRC32C instructions.
-
-#ifndef CRC32C_CRC32C_ARM_LINUX_CHECK_H_
-#define CRC32C_CRC32C_ARM_LINUX_CHECK_H_
-
-// X86-specific code checking for the availability of SSE4.2 instructions.
-
-#include <cstddef>
-#include <cstdint>
-
-#include "crc32c/crc32c_config.h"
-
-#if HAVE_ARM64_CRC32C
-
-#if HAVE_STRONG_GETAUXVAL
-#include <sys/auxv.h>
-#elif HAVE_WEAK_GETAUXVAL
-// getauxval() is not available on Android until API level 20. Link it as a weak
-// symbol.
-extern "C" unsigned long getauxval(unsigned long type) __attribute__((weak));
-
-#define AT_HWCAP 16
-#endif  // HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL
-
-namespace crc32c {
-
-inline bool CanUseArm64Linux() {
-#if HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL
-  // From 'arch/arm64/include/uapi/asm/hwcap.h' in Linux kernel source code.
-  constexpr unsigned long kHWCAP_PMULL = 1 << 4;
-  constexpr unsigned long kHWCAP_CRC32 = 1 << 7;
-  unsigned long hwcap = (&getauxval != nullptr) ? getauxval(AT_HWCAP) : 0;
-  return (hwcap & (kHWCAP_PMULL | kHWCAP_CRC32)) ==
-         (kHWCAP_PMULL | kHWCAP_CRC32);
-#else
-  return false;
-#endif  // HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL
-}
-
-}  // namespace crc32c
-
-#endif  // HAVE_ARM64_CRC32C
-
-#endif  // CRC32C_CRC32C_ARM_LINUX_CHECK_H_
diff --git a/include/crc32c_read_le.h b/include/crc32c_read_le.h
--- a/include/crc32c_read_le.h
+++ b/include/crc32c_read_le.h
@@ -30,14 +30,14 @@
 // Reads a little-endian 64-bit integer from a 64-bit-aligned buffer.
 inline uint64_t ReadUint64LE(const uint8_t* buffer) {
 #if BYTE_ORDER_BIG_ENDIAN
-  return ((static_cast<uint32_t>(static_cast<uint8_t>(buffer[0]))) |
-          (static_cast<uint32_t>(static_cast<uint8_t>(buffer[1])) << 8) |
-          (static_cast<uint32_t>(static_cast<uint8_t>(buffer[2])) << 16) |
-          (static_cast<uint32_t>(static_cast<uint8_t>(buffer[3])) << 24) |
-          (static_cast<uint32_t>(static_cast<uint8_t>(buffer[4])) << 32) |
-          (static_cast<uint32_t>(static_cast<uint8_t>(buffer[5])) << 40) |
-          (static_cast<uint32_t>(static_cast<uint8_t>(buffer[6])) << 48) |
-          (static_cast<uint32_t>(static_cast<uint8_t>(buffer[7])) << 56));
+  return ((static_cast<uint64_t>(static_cast<uint8_t>(buffer[0]))) |
+          (static_cast<uint64_t>(static_cast<uint8_t>(buffer[1])) << 8) |
+          (static_cast<uint64_t>(static_cast<uint8_t>(buffer[2])) << 16) |
+          (static_cast<uint64_t>(static_cast<uint8_t>(buffer[3])) << 24) |
+          (static_cast<uint64_t>(static_cast<uint8_t>(buffer[4])) << 32) |
+          (static_cast<uint64_t>(static_cast<uint8_t>(buffer[5])) << 40) |
+          (static_cast<uint64_t>(static_cast<uint8_t>(buffer[6])) << 48) |
+          (static_cast<uint64_t>(static_cast<uint8_t>(buffer[7])) << 56));
 #else   // !BYTE_ORDER_BIG_ENDIAN
   uint64_t result;
   // This should be optimized to a single instruction.
diff --git a/include/crc32c_sse42.cc b/include/crc32c_sse42.cc
--- a/include/crc32c_sse42.cc
+++ b/include/crc32c_sse42.cc
@@ -174,7 +174,7 @@
     }
   }
 
-  // Proccess the data in predetermined block sizes with tables for quickly
+  // Process the data in predetermined block sizes with tables for quickly
   // combining the checksum. Experimentally it's better to use larger block
   // sizes where possible so use a hierarchy of decreasing block sizes.
   uint64_t l64 = l;
diff --git a/src/Data/Digest/CRC32C.hs b/src/Data/Digest/CRC32C.hs
--- a/src/Data/Digest/CRC32C.hs
+++ b/src/Data/Digest/CRC32C.hs
@@ -3,16 +3,56 @@
   , crc32c_update
   ) where
 
-import           Data.ByteString.Internal    (ByteString (..))
-import           Data.Digest.CRC32C.Internal
+import           Data.ByteString (ByteString)
+import           Data.ByteString.Unsafe (unsafeUseAsCStringLen)
 import           Data.Word
-import           Foreign.C.Types
-import           Foreign.ForeignPtr.Unsafe
-import           Foreign.Ptr
 
+import           Foreign
+import           Foreign.C
+import           Foreign.Marshal.Unsafe
+
 crc32c :: ByteString -> Word32
-crc32c (PS p o l) = fromIntegral $ lib_crc32c_value (unsafeForeignPtrToPtr p `plusPtr` o) (fromIntegral l)
+crc32c bs =
+  fromIntegral $
+    unsafeLocalState $
+      unsafeUseAsCStringLen bs $ \(p, l) ->
+        lib_crc32c_value (castPtr p) (fromIntegral l)
 
 crc32c_update :: Word32 -> ByteString -> Word32
-crc32c_update hash (PS p o l) = fromIntegral $ lib_crc32c_extend (CUInt hash) (unsafeForeignPtrToPtr p `plusPtr` o) (fromIntegral l)
+crc32c_update hash bs =
+  fromIntegral $
+    unsafeLocalState $
+      unsafeUseAsCStringLen bs $ \(p, l) ->
+        lib_crc32c_extend (fromIntegral hash) (castPtr p) (fromIntegral l)
 
+-- FFI: safe or unsafe?
+-- All of the FFI here is not-reentrant so we don't have to use safe. However
+-- for very large inputs, the calls will take a while and during that time
+-- they will block GC and other Haskell threads on the same capability. So
+-- for very large inputs we would prefer to use a safe FFI call. But for small
+-- inputs, the overhead of a safe FFI call is quite substantial, e.g
+-- ~5x for 256 bytes, dropping to only ~10% for 16kb.
+--
+-- The solution we use here is to use unsafe FFI calls for smaller buffers and
+-- safe FFI calls for larger buffers. This bounds the time that these calls
+-- can block other threads or GC.
+
+lib_crc32c_extend :: CUInt -> Ptr CUChar -> CSize -> IO CUInt
+lib_crc32c_extend hash p l | l > 0x10000 = lib_crc32c_extend_safe hash p l
+                           | otherwise   = lib_crc32c_extend_unsafe hash p l
+
+lib_crc32c_value :: Ptr CUChar -> CSize -> IO CUInt
+lib_crc32c_value p l | l > 0x10000 = lib_crc32c_value_safe p l
+                     | otherwise   = lib_crc32c_value_unsafe p l
+
+foreign import ccall unsafe "crc32c/crc32c.h crc32c_extend"
+  lib_crc32c_extend_unsafe :: CUInt -> Ptr CUChar -> CSize -> IO CUInt
+
+foreign import ccall safe "crc32c/crc32c.h crc32c_extend"
+  lib_crc32c_extend_safe :: CUInt -> Ptr CUChar -> CSize -> IO CUInt
+
+foreign import ccall unsafe "crc32c/crc32c.h crc32c_value"
+  lib_crc32c_value_unsafe :: Ptr CUChar -> CSize -> IO CUInt
+
+foreign import ccall safe "crc32c/crc32c.h crc32c_value"
+  lib_crc32c_value_safe :: Ptr CUChar -> CSize -> IO CUInt
diff --git a/src/Data/Digest/CRC32C/Internal.chs b/src/Data/Digest/CRC32C/Internal.chs
deleted file mode 100644
--- a/src/Data/Digest/CRC32C/Internal.chs
+++ /dev/null
@@ -1,12 +0,0 @@
-module Data.Digest.CRC32C.Internal where
-
-#include <crc32c/crc32c.h>
-
-import Foreign.C
-import Foreign.Ptr
-
-lib_crc32c_extend :: CUInt -> Ptr CUChar -> CULong -> CUInt
-lib_crc32c_extend = {#call pure crc32c_extend #}
-
-lib_crc32c_value :: Ptr CUChar -> CULong -> CUInt
-lib_crc32c_value  = {#call pure crc32c_value  #}
