crc32c 0.0.0 → 0.2.2
raw patch · 14 files changed
Files
- ChangeLog.md +18/−0
- README.md +1/−2
- bench/Bench.hs +33/−0
- crc32c.cabal +35/−32
- include/crc32c.cc +3/−3
- include/crc32c/crc32c_config.h +2/−0
- include/crc32c_arm64.cc +11/−12
- include/crc32c_arm64.h +4/−4
- include/crc32c_arm64_check.h +66/−0
- include/crc32c_arm64_linux_check.h +0/−48
- include/crc32c_read_le.h +8/−8
- include/crc32c_sse42.cc +1/−1
- src/Data/Digest/CRC32C.hs +47/−7
- src/Data/Digest/CRC32C/Internal.chs +0/−12
+ ChangeLog.md view
@@ -0,0 +1,18 @@+0.2.2+---++- Use system-cxx-std-lib for specifying C++ linkage++++0.2.1+---++- Fix build on aarch64.++++0.2.0+---++- Fix MacOS, Windows Build
README.md view
@@ -1,4 +1,3 @@-# crc32c-[](https://travis-ci.org/leptonyu/crc32c)+# crc32c [](https://hackage.haskell.org/package/crc32c) [](https://travis-ci.org/leptonyu/crc32c) Haskell bindings for [crc32c](https://github.com/google/crc32c)
+ bench/Bench.hs view
@@ -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)+
crc32c.cabal view
@@ -1,27 +1,23 @@-cabal-version: 1.12---- This file has been generated from package.yaml by hpack version 0.31.1.------ see: https://github.com/sol/hpack------ hash: 90e286fde5d013c853cdb6ad9ef6c03ad8eea828fa98d136baf253d357d78dee+cabal-version: 2.2 name: crc32c-version: 0.0.0-synopsis: Haskell bindings for crc32c+version: 0.2.2+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,52 @@ 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 && <0.11- if arch(x86_64)- cc-options: -std=c++11 -D__HAVE_SSE42=1 -D__HAVE_ARM64_CRC32C=0 -msse4.2+ cxx-options: -std=c++11+ if impl(ghc >= 9.4)+ build-depends: system-cxx-std-lib 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- default-language: Haskell2010+ extra-libraries:+ stdc+++ 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
include/crc32c.cc view
@@ -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);
include/crc32c/crc32c_config.h view
@@ -12,7 +12,9 @@ #define HAVE_BUILTIN_PREFETCH 1 // Define to 1 if targeting X86 and the compiler has the _mm_prefetch intrinsic.+#if (defined(_M_X64) || defined(__x86_64__)) #define HAVE_MM_PREFETCH 1+#endif // Define to 1 if the system libraries have the getauxval function in the // <sys/auxv.h> header. Should be true on Linux and Android API level 20+.
include/crc32c_arm64.cc view
@@ -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;
include/crc32c_arm64.h view
@@ -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_
+ include/crc32c_arm64_check.h view
@@ -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_
− include/crc32c_arm64_linux_check.h
@@ -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_
include/crc32c_read_le.h view
@@ -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.
include/crc32c_sse42.cc view
@@ -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;
src/Data/Digest/CRC32C.hs view
@@ -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
− src/Data/Digest/CRC32C/Internal.chs
@@ -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 #}