packages feed

xxhash-ffi (empty) → 0.1.0.0

raw patch · 8 files changed

+1284/−0 lines, 8 filesdep +QuickCheckdep +basedep +bytestringsetup-changed

Dependencies added: QuickCheck, base, bytestring, criterion, deepseq, digest, hashable, hspec, murmur-hash, xxhash, xxhash-ffi

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Henri Verroken (c) 2017++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Henri Verroken nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,9 @@+xxhash-ffi+==========++[![Build Status](https://travis-ci.org/haskell-haskey/xxhash-ffi.svg?branch=master)](https://travis-ci.org/haskell-haskey/xxhash-ffi)+[![Hackage](https://img.shields.io/hackage/v/xxhash-ffi.svg?maxAge=2592000)](https://hackage.haskell.org/package/xxhash-ffi)+[![Stackage Nightly](http://stackage.org/package/xxhash-ffi/badge/nightly)](http://stackage.org/nightly/package/xxhash-ffi)+[![Stackage LTS](http://stackage.org/package/xxhash-ffi/badge/lts)](http://stackage.org/lts/package/xxhash-ffi)++Fastest Haskell FFI bindings to the xxHash library. The C implementation is directly taken from [xxHash](https://github.com/Cyan4973/xxHash).
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ bench/Bench.hs view
@@ -0,0 +1,54 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+module Main where++import Prelude hiding (words)++import qualified Data.ByteString as B+import qualified Data.Digest.Adler32 as Adler32+import qualified Data.Digest.CRC32 as CRC32+import qualified Data.Digest.XXHash as XXHash+import qualified Data.Digest.XXHash.FFI as FFI+import qualified Data.Hashable as Hashable++import Criterion.Main++main :: IO ()+main = do+    -- apt-get install wamerican+    med <- B.readFile "/usr/share/dict/words"++    defaultMain+        [ bgroup "1MB"+            [ bench "Data.Digest.XXHash.FFI (32) (c)" $ nf (FFI.xxh32 med) 0+            , bench "Data.Digest.XXHash.FFI (64) (c)" $ nf (FFI.xxh64 med) 0+            , bench "xxhash (c)" $ nf XXHash.c_xxHash' med+            , bench "Data.Digest.XXHash (haskell)" $ nf XXHash.xxHash' med+            , bench "Data.Digest.adler32 (c, zlib)" $ nf Adler32.adler32 med+            , bench "Data.Digest.crc32 (c, zlib)" $ nf CRC32.crc32 med+            , bench "Data.Hashable (c, FNV?)" $ nf Hashable.hash med+            ]+        , bgroup "1kB"+            [ bench "Data.Digest.XXHash.FFI (32) (c)" $ nf (FFI.xxh32 bs1k) 0+            , bench "Data.Digest.XXHash.FFI (64) (c)" $ nf (FFI.xxh64 bs1k) 0+            , bench "xxhash (c)" $ nf XXHash.c_xxHash' bs1k+            , bench "Data.Digest.XXHash (haskell)" $ nf XXHash.xxHash' bs1k+            , bench "Data.Digest.adler32 (c, zlib)" $ nf Adler32.adler32 bs1k+            , bench "Data.Digest.crc32 (c, zlib)" $ nf CRC32.crc32 bs1k+            , bench "Data.Hashable (c, FNV?)" $ nf Hashable.hash bs1k+            ]+        , bgroup "4kB"+            [ bench "Data.Digest.XXHash.FFI (32) (c)" $ nf (FFI.xxh32 bs4k) 0+            , bench "Data.Digest.XXHash.FFI (64) (c)" $ nf (FFI.xxh64 bs4k) 0+            , bench "xxhash (c)" $ nf XXHash.c_xxHash' bs4k+            , bench "Data.Digest.XXHash (haskell)" $ nf XXHash.xxHash' bs4k+            , bench "Data.Digest.adler32 (c, zlib)" $ nf Adler32.adler32 bs4k+            , bench "Data.Digest.crc32 (c, zlib)" $ nf CRC32.crc32 bs4k+            , bench "Data.Hashable (c, FNV?)" $ nf Hashable.hash bs4k+            ]+        ]++bs1k :: B.ByteString+bs1k = fst $ B.unfoldrN 1024 (\x -> Just (x, x)) 0++bs4k :: B.ByteString+bs4k = fst $ B.unfoldrN 4096 (\x -> Just (x, x)) 0
+ src-c/xxhash.c view
@@ -0,0 +1,888 @@+/*+*  xxHash - Fast Hash algorithm+*  Copyright (C) 2012-2016, Yann Collet+*+*  BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)+*+*  Redistribution and use in source and binary forms, with or without+*  modification, are permitted provided that the following conditions are+*  met:+*+*  * Redistributions of source code must retain the above copyright+*  notice, this list of conditions and the following disclaimer.+*  * Redistributions in binary form must reproduce the above+*  copyright notice, this list of conditions and the following disclaimer+*  in the documentation and/or other materials provided with the+*  distribution.+*+*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+*  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+*  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+*  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+*  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+*  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+*  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+*+*  You can contact the author at :+*  - xxHash homepage: http://www.xxhash.com+*  - xxHash source repository : https://github.com/Cyan4973/xxHash+*/+++/* *************************************+*  Tuning parameters+***************************************/+/*!XXH_FORCE_MEMORY_ACCESS :+ * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.+ * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.+ * The below switch allow to select different access method for improved performance.+ * Method 0 (default) : use `memcpy()`. Safe and portable.+ * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).+ *            This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.+ * Method 2 : direct access. This method doesn't depend on compiler but violate C standard.+ *            It can generate buggy code on targets which do not support unaligned memory accesses.+ *            But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)+ * See http://stackoverflow.com/a/32095106/646947 for details.+ * Prefer these methods in priority order (0 > 1 > 2)+ */+#ifndef XXH_FORCE_MEMORY_ACCESS   /* can be defined externally, on command line for example */+#  if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )+#    define XXH_FORCE_MEMORY_ACCESS 2+#  elif defined(__INTEL_COMPILER) || \+  (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) ))+#    define XXH_FORCE_MEMORY_ACCESS 1+#  endif+#endif++/*!XXH_ACCEPT_NULL_INPUT_POINTER :+ * If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer.+ * When this option is enabled, xxHash output for null input pointers will be the same as a null-length input.+ * By default, this option is disabled. To enable it, uncomment below define :+ */+/* #define XXH_ACCEPT_NULL_INPUT_POINTER 1 */++/*!XXH_FORCE_NATIVE_FORMAT :+ * By default, xxHash library provides endian-independent Hash values, based on little-endian convention.+ * Results are therefore identical for little-endian and big-endian CPU.+ * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.+ * Should endian-independence be of no importance for your application, you may set the #define below to 1,+ * to improve speed for Big-endian CPU.+ * This option has no impact on Little_Endian CPU.+ */+#ifndef XXH_FORCE_NATIVE_FORMAT   /* can be defined externally */+#  define XXH_FORCE_NATIVE_FORMAT 0+#endif++/*!XXH_FORCE_ALIGN_CHECK :+ * This is a minor performance trick, only useful with lots of very small keys.+ * It means : check for aligned/unaligned input.+ * The check costs one initial branch per hash;+ * set it to 0 when the input is guaranteed to be aligned,+ * or when alignment doesn't matter for performance.+ */+#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */+#  if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)+#    define XXH_FORCE_ALIGN_CHECK 0+#  else+#    define XXH_FORCE_ALIGN_CHECK 1+#  endif+#endif+++/* *************************************+*  Includes & Memory related functions+***************************************/+/*! Modify the local functions below should you wish to use some other memory routines+*   for malloc(), free() */+#include <stdlib.h>+static void* XXH_malloc(size_t s) { return malloc(s); }+static void  XXH_free  (void* p)  { free(p); }+/*! and for memcpy() */+#include <string.h>+static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); }++#define XXH_STATIC_LINKING_ONLY+#include "xxhash.h"+++/* *************************************+*  Compiler Specific Options+***************************************/+#ifdef _MSC_VER    /* Visual Studio */+#  pragma warning(disable : 4127)      /* disable: C4127: conditional expression is constant */+#  define FORCE_INLINE static __forceinline+#else+#  if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */+#    ifdef __GNUC__+#      define FORCE_INLINE static inline __attribute__((always_inline))+#    else+#      define FORCE_INLINE static inline+#    endif+#  else+#    define FORCE_INLINE static+#  endif /* __STDC_VERSION__ */+#endif+++/* *************************************+*  Basic Types+***************************************/+#ifndef MEM_MODULE+# if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )+#   include <stdint.h>+    typedef uint8_t  BYTE;+    typedef uint16_t U16;+    typedef uint32_t U32;+# else+    typedef unsigned char      BYTE;+    typedef unsigned short     U16;+    typedef unsigned int       U32;+# endif+#endif++#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))++/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */+static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; }++#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))++/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */+/* currently only defined for gcc and icc */+typedef union { U32 u32; } __attribute__((packed)) unalign;+static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }++#else++/* portable and safe solution. Generally efficient.+ * see : http://stackoverflow.com/a/32095106/646947+ */+static U32 XXH_read32(const void* memPtr)+{+    U32 val;+    memcpy(&val, memPtr, sizeof(val));+    return val;+}++#endif   /* XXH_FORCE_DIRECT_MEMORY_ACCESS */+++/* ****************************************+*  Compiler-specific Functions and Macros+******************************************/+#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)++/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */+#if defined(_MSC_VER)+#  define XXH_rotl32(x,r) _rotl(x,r)+#  define XXH_rotl64(x,r) _rotl64(x,r)+#else+#  define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))+#  define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r)))+#endif++#if defined(_MSC_VER)     /* Visual Studio */+#  define XXH_swap32 _byteswap_ulong+#elif XXH_GCC_VERSION >= 403+#  define XXH_swap32 __builtin_bswap32+#else+static U32 XXH_swap32 (U32 x)+{+    return  ((x << 24) & 0xff000000 ) |+            ((x <<  8) & 0x00ff0000 ) |+            ((x >>  8) & 0x0000ff00 ) |+            ((x >> 24) & 0x000000ff );+}+#endif+++/* *************************************+*  Architecture Macros+***************************************/+typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;++/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */+#ifndef XXH_CPU_LITTLE_ENDIAN+    static const int g_one = 1;+#   define XXH_CPU_LITTLE_ENDIAN   (*(const char*)(&g_one))+#endif+++/* ***************************+*  Memory reads+*****************************/+typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;++FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align)+{+    if (align==XXH_unaligned)+        return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));+    else+        return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr);+}++FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian)+{+    return XXH_readLE32_align(ptr, endian, XXH_unaligned);+}++static U32 XXH_readBE32(const void* ptr)+{+    return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr);+}+++/* *************************************+*  Macros+***************************************/+#define XXH_STATIC_ASSERT(c)   { enum { XXH_static_assert = 1/(int)(!!(c)) }; }    /* use only *after* variable declarations */+XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; }+++/* *******************************************************************+*  32-bits hash functions+*********************************************************************/+static const U32 PRIME32_1 = 2654435761U;+static const U32 PRIME32_2 = 2246822519U;+static const U32 PRIME32_3 = 3266489917U;+static const U32 PRIME32_4 =  668265263U;+static const U32 PRIME32_5 =  374761393U;++static U32 XXH32_round(U32 seed, U32 input)+{+    seed += input * PRIME32_2;+    seed  = XXH_rotl32(seed, 13);+    seed *= PRIME32_1;+    return seed;+}++FORCE_INLINE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align)+{+    const BYTE* p = (const BYTE*)input;+    const BYTE* bEnd = p + len;+    U32 h32;+#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align)++#ifdef XXH_ACCEPT_NULL_INPUT_POINTER+    if (p==NULL) {+        len=0;+        bEnd=p=(const BYTE*)(size_t)16;+    }+#endif++    if (len>=16) {+        const BYTE* const limit = bEnd - 16;+        U32 v1 = seed + PRIME32_1 + PRIME32_2;+        U32 v2 = seed + PRIME32_2;+        U32 v3 = seed + 0;+        U32 v4 = seed - PRIME32_1;++        do {+            v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4;+            v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4;+            v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4;+            v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4;+        } while (p<=limit);++        h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);+    } else {+        h32  = seed + PRIME32_5;+    }++    h32 += (U32) len;++    while (p+4<=bEnd) {+        h32 += XXH_get32bits(p) * PRIME32_3;+        h32  = XXH_rotl32(h32, 17) * PRIME32_4 ;+        p+=4;+    }++    while (p<bEnd) {+        h32 += (*p) * PRIME32_5;+        h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;+        p++;+    }++    h32 ^= h32 >> 15;+    h32 *= PRIME32_2;+    h32 ^= h32 >> 13;+    h32 *= PRIME32_3;+    h32 ^= h32 >> 16;++    return h32;+}+++XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed)+{+#if 0+    /* Simple version, good for code maintenance, but unfortunately slow for small inputs */+    XXH32_state_t state;+    XXH32_reset(&state, seed);+    XXH32_update(&state, input, len);+    return XXH32_digest(&state);+#else+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;++    if (XXH_FORCE_ALIGN_CHECK) {+        if ((((size_t)input) & 3) == 0) {   /* Input is 4-bytes aligned, leverage the speed benefit */+            if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+                return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);+            else+                return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);+    }   }++    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+        return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);+    else+        return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);+#endif+}++++/*======   Hash streaming   ======*/++XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void)+{+    return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t));+}+XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr)+{+    XXH_free(statePtr);+    return XXH_OK;+}++XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState)+{+    memcpy(dstState, srcState, sizeof(*dstState));+}++XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed)+{+    XXH32_state_t state;   /* using a local state to memcpy() in order to avoid strict-aliasing warnings */+    memset(&state, 0, sizeof(state)-4);   /* do not write into reserved, for future removal */+    state.v1 = seed + PRIME32_1 + PRIME32_2;+    state.v2 = seed + PRIME32_2;+    state.v3 = seed + 0;+    state.v4 = seed - PRIME32_1;+    memcpy(statePtr, &state, sizeof(state));+    return XXH_OK;+}+++FORCE_INLINE XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian)+{+    const BYTE* p = (const BYTE*)input;+    const BYTE* const bEnd = p + len;++#ifdef XXH_ACCEPT_NULL_INPUT_POINTER+    if (input==NULL) return XXH_ERROR;+#endif++    state->total_len_32 += (unsigned)len;+    state->large_len |= (len>=16) | (state->total_len_32>=16);++    if (state->memsize + len < 16)  {   /* fill in tmp buffer */+        XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len);+        state->memsize += (unsigned)len;+        return XXH_OK;+    }++    if (state->memsize) {   /* some data left from previous update */+        XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize);+        {   const U32* p32 = state->mem32;+            state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++;+            state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++;+            state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++;+            state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian));+        }+        p += 16-state->memsize;+        state->memsize = 0;+    }++    if (p <= bEnd-16) {+        const BYTE* const limit = bEnd - 16;+        U32 v1 = state->v1;+        U32 v2 = state->v2;+        U32 v3 = state->v3;+        U32 v4 = state->v4;++        do {+            v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4;+            v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4;+            v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4;+            v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4;+        } while (p<=limit);++        state->v1 = v1;+        state->v2 = v2;+        state->v3 = v3;+        state->v4 = v4;+    }++    if (p < bEnd) {+        XXH_memcpy(state->mem32, p, (size_t)(bEnd-p));+        state->memsize = (unsigned)(bEnd-p);+    }++    return XXH_OK;+}++XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len)+{+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;++    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+        return XXH32_update_endian(state_in, input, len, XXH_littleEndian);+    else+        return XXH32_update_endian(state_in, input, len, XXH_bigEndian);+}++++FORCE_INLINE U32 XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian)+{+    const BYTE * p = (const BYTE*)state->mem32;+    const BYTE* const bEnd = (const BYTE*)(state->mem32) + state->memsize;+    U32 h32;++    if (state->large_len) {+        h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18);+    } else {+        h32 = state->v3 /* == seed */ + PRIME32_5;+    }++    h32 += state->total_len_32;++    while (p+4<=bEnd) {+        h32 += XXH_readLE32(p, endian) * PRIME32_3;+        h32  = XXH_rotl32(h32, 17) * PRIME32_4;+        p+=4;+    }++    while (p<bEnd) {+        h32 += (*p) * PRIME32_5;+        h32  = XXH_rotl32(h32, 11) * PRIME32_1;+        p++;+    }++    h32 ^= h32 >> 15;+    h32 *= PRIME32_2;+    h32 ^= h32 >> 13;+    h32 *= PRIME32_3;+    h32 ^= h32 >> 16;++    return h32;+}+++XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in)+{+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;++    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+        return XXH32_digest_endian(state_in, XXH_littleEndian);+    else+        return XXH32_digest_endian(state_in, XXH_bigEndian);+}+++/*======   Canonical representation   ======*/++/*! Default XXH result types are basic unsigned 32 and 64 bits.+*   The canonical representation follows human-readable write convention, aka big-endian (large digits first).+*   These functions allow transformation of hash result into and from its canonical format.+*   This way, hash values can be written into a file or buffer, and remain comparable across different systems and programs.+*/++XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash)+{+    XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t));+    if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash);+    memcpy(dst, &hash, sizeof(*dst));+}++XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src)+{+    return XXH_readBE32(src);+}+++#ifndef XXH_NO_LONG_LONG++/* *******************************************************************+*  64-bits hash functions+*********************************************************************/++/*======   Memory access   ======*/++#ifndef MEM_MODULE+# define MEM_MODULE+# if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )+#   include <stdint.h>+    typedef uint64_t U64;+# else+    typedef unsigned long long U64;   /* if your compiler doesn't support unsigned long long, replace by another 64-bit type here. Note that xxhash.h will also need to be updated. */+# endif+#endif+++#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))++/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */+static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; }++#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))++/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */+/* currently only defined for gcc and icc */+typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64;+static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; }++#else++/* portable and safe solution. Generally efficient.+ * see : http://stackoverflow.com/a/32095106/646947+ */++static U64 XXH_read64(const void* memPtr)+{+    U64 val;+    memcpy(&val, memPtr, sizeof(val));+    return val;+}++#endif   /* XXH_FORCE_DIRECT_MEMORY_ACCESS */++#if defined(_MSC_VER)     /* Visual Studio */+#  define XXH_swap64 _byteswap_uint64+#elif XXH_GCC_VERSION >= 403+#  define XXH_swap64 __builtin_bswap64+#else+static U64 XXH_swap64 (U64 x)+{+    return  ((x << 56) & 0xff00000000000000ULL) |+            ((x << 40) & 0x00ff000000000000ULL) |+            ((x << 24) & 0x0000ff0000000000ULL) |+            ((x << 8)  & 0x000000ff00000000ULL) |+            ((x >> 8)  & 0x00000000ff000000ULL) |+            ((x >> 24) & 0x0000000000ff0000ULL) |+            ((x >> 40) & 0x000000000000ff00ULL) |+            ((x >> 56) & 0x00000000000000ffULL);+}+#endif++FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align)+{+    if (align==XXH_unaligned)+        return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));+    else+        return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr);+}++FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian)+{+    return XXH_readLE64_align(ptr, endian, XXH_unaligned);+}++static U64 XXH_readBE64(const void* ptr)+{+    return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr);+}+++/*======   xxh64   ======*/++static const U64 PRIME64_1 = 11400714785074694791ULL;+static const U64 PRIME64_2 = 14029467366897019727ULL;+static const U64 PRIME64_3 =  1609587929392839161ULL;+static const U64 PRIME64_4 =  9650029242287828579ULL;+static const U64 PRIME64_5 =  2870177450012600261ULL;++static U64 XXH64_round(U64 acc, U64 input)+{+    acc += input * PRIME64_2;+    acc  = XXH_rotl64(acc, 31);+    acc *= PRIME64_1;+    return acc;+}++static U64 XXH64_mergeRound(U64 acc, U64 val)+{+    val  = XXH64_round(0, val);+    acc ^= val;+    acc  = acc * PRIME64_1 + PRIME64_4;+    return acc;+}++FORCE_INLINE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align)+{+    const BYTE* p = (const BYTE*)input;+    const BYTE* bEnd = p + len;+    U64 h64;+#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align)++#ifdef XXH_ACCEPT_NULL_INPUT_POINTER+    if (p==NULL) {+        len=0;+        bEnd=p=(const BYTE*)(size_t)32;+    }+#endif++    if (len>=32) {+        const BYTE* const limit = bEnd - 32;+        U64 v1 = seed + PRIME64_1 + PRIME64_2;+        U64 v2 = seed + PRIME64_2;+        U64 v3 = seed + 0;+        U64 v4 = seed - PRIME64_1;++        do {+            v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8;+            v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8;+            v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8;+            v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8;+        } while (p<=limit);++        h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);+        h64 = XXH64_mergeRound(h64, v1);+        h64 = XXH64_mergeRound(h64, v2);+        h64 = XXH64_mergeRound(h64, v3);+        h64 = XXH64_mergeRound(h64, v4);++    } else {+        h64  = seed + PRIME64_5;+    }++    h64 += (U64) len;++    while (p+8<=bEnd) {+        U64 const k1 = XXH64_round(0, XXH_get64bits(p));+        h64 ^= k1;+        h64  = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4;+        p+=8;+    }++    if (p+4<=bEnd) {+        h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1;+        h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;+        p+=4;+    }++    while (p<bEnd) {+        h64 ^= (*p) * PRIME64_5;+        h64 = XXH_rotl64(h64, 11) * PRIME64_1;+        p++;+    }++    h64 ^= h64 >> 33;+    h64 *= PRIME64_2;+    h64 ^= h64 >> 29;+    h64 *= PRIME64_3;+    h64 ^= h64 >> 32;++    return h64;+}+++XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed)+{+#if 0+    /* Simple version, good for code maintenance, but unfortunately slow for small inputs */+    XXH64_state_t state;+    XXH64_reset(&state, seed);+    XXH64_update(&state, input, len);+    return XXH64_digest(&state);+#else+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;++    if (XXH_FORCE_ALIGN_CHECK) {+        if ((((size_t)input) & 7)==0) {  /* Input is aligned, let's leverage the speed advantage */+            if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+                return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);+            else+                return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);+    }   }++    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+        return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);+    else+        return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);+#endif+}++/*======   Hash Streaming   ======*/++XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void)+{+    return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t));+}+XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr)+{+    XXH_free(statePtr);+    return XXH_OK;+}++XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState)+{+    memcpy(dstState, srcState, sizeof(*dstState));+}++XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed)+{+    XXH64_state_t state;   /* using a local state to memcpy() in order to avoid strict-aliasing warnings */+    memset(&state, 0, sizeof(state)-8);   /* do not write into reserved, for future removal */+    state.v1 = seed + PRIME64_1 + PRIME64_2;+    state.v2 = seed + PRIME64_2;+    state.v3 = seed + 0;+    state.v4 = seed - PRIME64_1;+    memcpy(statePtr, &state, sizeof(state));+    return XXH_OK;+}++FORCE_INLINE XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian)+{+    const BYTE* p = (const BYTE*)input;+    const BYTE* const bEnd = p + len;++#ifdef XXH_ACCEPT_NULL_INPUT_POINTER+    if (input==NULL) return XXH_ERROR;+#endif++    state->total_len += len;++    if (state->memsize + len < 32) {  /* fill in tmp buffer */+        XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len);+        state->memsize += (U32)len;+        return XXH_OK;+    }++    if (state->memsize) {   /* tmp buffer is full */+        XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize);+        state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian));+        state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian));+        state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian));+        state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian));+        p += 32-state->memsize;+        state->memsize = 0;+    }++    if (p+32 <= bEnd) {+        const BYTE* const limit = bEnd - 32;+        U64 v1 = state->v1;+        U64 v2 = state->v2;+        U64 v3 = state->v3;+        U64 v4 = state->v4;++        do {+            v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8;+            v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8;+            v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8;+            v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8;+        } while (p<=limit);++        state->v1 = v1;+        state->v2 = v2;+        state->v3 = v3;+        state->v4 = v4;+    }++    if (p < bEnd) {+        XXH_memcpy(state->mem64, p, (size_t)(bEnd-p));+        state->memsize = (unsigned)(bEnd-p);+    }++    return XXH_OK;+}++XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len)+{+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;++    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+        return XXH64_update_endian(state_in, input, len, XXH_littleEndian);+    else+        return XXH64_update_endian(state_in, input, len, XXH_bigEndian);+}++FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian)+{+    const BYTE * p = (const BYTE*)state->mem64;+    const BYTE* const bEnd = (const BYTE*)state->mem64 + state->memsize;+    U64 h64;++    if (state->total_len >= 32) {+        U64 const v1 = state->v1;+        U64 const v2 = state->v2;+        U64 const v3 = state->v3;+        U64 const v4 = state->v4;++        h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);+        h64 = XXH64_mergeRound(h64, v1);+        h64 = XXH64_mergeRound(h64, v2);+        h64 = XXH64_mergeRound(h64, v3);+        h64 = XXH64_mergeRound(h64, v4);+    } else {+        h64  = state->v3 + PRIME64_5;+    }++    h64 += (U64) state->total_len;++    while (p+8<=bEnd) {+        U64 const k1 = XXH64_round(0, XXH_readLE64(p, endian));+        h64 ^= k1;+        h64  = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4;+        p+=8;+    }++    if (p+4<=bEnd) {+        h64 ^= (U64)(XXH_readLE32(p, endian)) * PRIME64_1;+        h64  = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;+        p+=4;+    }++    while (p<bEnd) {+        h64 ^= (*p) * PRIME64_5;+        h64  = XXH_rotl64(h64, 11) * PRIME64_1;+        p++;+    }++    h64 ^= h64 >> 33;+    h64 *= PRIME64_2;+    h64 ^= h64 >> 29;+    h64 *= PRIME64_3;+    h64 ^= h64 >> 32;++    return h64;+}++XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in)+{+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;++    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)+        return XXH64_digest_endian(state_in, XXH_littleEndian);+    else+        return XXH64_digest_endian(state_in, XXH_bigEndian);+}+++/*====== Canonical representation   ======*/++XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash)+{+    XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t));+    if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash);+    memcpy(dst, &hash, sizeof(*dst));+}++XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src)+{+    return XXH_readBE64(src);+}++#endif  /* XXH_NO_LONG_LONG */
+ src/Data/Digest/XXHash/FFI.hs view
@@ -0,0 +1,180 @@+{-# LANGUAGE ForeignFunctionInterface #-}+-- |+-- Module:     Data.Digest.XXHash.FFI+-- Copyright:  (c) 2017 Henri Verroken+-- Licence:    BSD3+-- Maintainer: Henri Verroken <henriverroken@gmail.com+-- Stability:  stable+--+-- This module provides bindings to the xxHash64 and the xxHash32 algorithm.+--+-- The C implementation used is directly taken from <https://github.com/Cyan4973/xxHash>.+module Data.Digest.XXHash.FFI (+  -- * Interface+  XXHash(..)++  -- * C Interface+  -- ** Direct Calculation+, c_xxh64+, c_xxh32++  -- ** 32-bit state functions+, XXH32State+, c_xxh32_createState+, c_xxh32_freeState+, c_xxh32_copyState+, c_xxh32_reset+, c_xxh32_update+, c_xxh32_digest++  -- ** 64-bit state functions+, XXH64State+, c_xxh64_createState+, c_xxh64_freeState+, c_xxh64_copyState+, c_xxh64_reset+, c_xxh64_update+, c_xxh64_digest+) where++import Control.Exception (bracket)++import Data.ByteString.Unsafe (unsafeUseAsCString)+import Data.Word (Word32, Word64)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BL++import Foreign.C+import Foreign.Ptr++import System.IO.Unsafe (unsafePerformIO)++foreign import ccall unsafe "XXH64" c_xxh64 ::+    Ptr a   -- ^ 'Ptr' to the input buffer+ -> CSize   -- ^ Buffer length+ -> CULLong -- ^ Seed+ -> CULLong -- ^ Resulting hash++foreign import ccall unsafe "XXH32" c_xxh32 ::+    Ptr a -- ^ 'Ptr' to the input buffer+ -> CSize -- ^ Buffer length+ -> CUInt -- ^ Seed+ -> CUInt -- ^ Resulting hash++data XXH32State++foreign import ccall unsafe "XXH32_createState" c_xxh32_createState ::+    IO (Ptr XXH32State) -- ^ Pointer to a newly allocated state++foreign import ccall unsafe "XXH32_freeState" c_xxh32_freeState ::+    Ptr XXH32State+ -> IO () -- ^ Free pointer allocated by 'c_xxh32_createState'++foreign import ccall unsafe "XXH32_copyState" c_xxh32_copyState ::+    Ptr XXH32State -- ^ Destination+ -> Ptr XXH32State -- ^ Source+ -> IO ()++foreign import ccall unsafe "XXH32_reset" c_xxh32_reset ::+    Ptr XXH32State -- ^ The state to reset+ -> CUInt          -- ^ The initial seed+ -> IO ()++foreign import ccall unsafe "XXH32_update" c_xxh32_update ::+    Ptr XXH32State -- ^ The state to update+ -> Ptr a          -- ^ 'Ptr' to the input buffer+ -> CSize          -- ^ Buffer length+ -> IO ()++foreign import ccall unsafe "XXH32_digest" c_xxh32_digest ::+    Ptr XXH32State -- ^ The state to digest+ -> IO CUInt       -- ^ Resulting hash++data XXH64State++foreign import ccall unsafe "XXH64_createState" c_xxh64_createState ::+    IO (Ptr XXH64State) -- ^ Pointer to a newly allocated state++foreign import ccall unsafe "XXH64_freeState" c_xxh64_freeState ::+    Ptr XXH64State+ -> IO () -- ^ Free pointer allocated by 'c_xxh64_createState'++foreign import ccall unsafe "XXH64_copyState" c_xxh64_copyState ::+    Ptr XXH64State -- ^ Destination+ -> Ptr XXH64State -- ^ Source+ -> IO ()++foreign import ccall unsafe "XXH64_reset" c_xxh64_reset ::+    Ptr XXH64State -- ^ The state to reset+ -> CULLong        -- ^ The initial seed+ -> IO ()++foreign import ccall unsafe "XXH64_update" c_xxh64_update ::+    Ptr XXH64State -- ^ The state to update+ -> Ptr a          -- ^ 'Ptr' to the input buffer+ -> CSize          -- ^ Buffer length+ -> IO ()++foreign import ccall unsafe "XXH64_digest" c_xxh64_digest ::+    Ptr XXH64State -- ^ The state to digest+ -> IO CULLong     -- ^ Resulting hash++-- | Class for hashable data types.+--+-- Not that all standard instances are specialized using the @SPECIALIZE@+-- pragma.+class XXHash t where+    -- | Calculate the 32-bit xxHash using a given seed.+    xxh32 :: t      -- ^ Data to hash+          -> Word32 -- ^ Seed+          -> Word32 -- ^ Resulting hash++    -- | Calculate the 64-bit xxHash using a given seed.+    xxh64 :: t      -- ^ Data to hash+          -> Word64 -- ^ Seed+          -> Word64 -- ^ Resulting hash+++instance XXHash BS.ByteString where+    xxh32 bs seed = unsafePerformIO $+        unsafeUseAsCString bs $ \ptr ->+            return . fromIntegral $ c_xxh32 (castPtr ptr) len (fromIntegral seed)+      where+        len = fromIntegral $ BS.length bs++    xxh64 bs seed = unsafePerformIO $+        unsafeUseAsCString bs $ \ptr ->+            return . fromIntegral $ c_xxh64 (castPtr ptr) len (fromIntegral seed)+      where+        len = fromIntegral $ BS.length bs++{-# SPECIALIZE xxh32 :: BS.ByteString -> Word32 -> Word32 #-}+{-# SPECIALIZE xxh64 :: BS.ByteString -> Word64 -> Word64 #-}++instance XXHash BL.ByteString where+    xxh32 bs seed = fromIntegral . unsafePerformIO $+        bracket c_xxh32_createState+                c_xxh32_freeState $ \state -> do+                    c_xxh32_reset state (fromIntegral seed)+                    mapM_ (update state) (BL.toChunks bs)+                    c_xxh32_digest state+      where+        update state bs' =+            let len = fromIntegral (BS.length bs') in+            unsafeUseAsCString bs' $ \ ptr ->+                c_xxh32_update state ptr len++    xxh64 bs seed = fromIntegral . unsafePerformIO $+        bracket c_xxh64_createState+                c_xxh64_freeState $ \state -> do+                    c_xxh64_reset state (fromIntegral seed)+                    mapM_ (update state) (BL.toChunks bs)+                    c_xxh64_digest state+      where+        update state bs' =+            let len = fromIntegral (BS.length bs') in+            unsafeUseAsCString bs' $ \ ptr ->+                c_xxh64_update state ptr len++{-# SPECIALIZE xxh32 :: BL.ByteString -> Word32 -> Word32 #-}+{-# SPECIALIZE xxh64 :: BL.ByteString -> Word64 -> Word64 #-}
+ test/Spec.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Main where++import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck++import Control.Applicative ((<$>))++import Data.ByteString (ByteString)+import Data.Word (Word32, Word64)+import qualified Data.ByteString.Lazy as BL++import Data.Digest.XXHash.FFI (xxh32, xxh64)++instance Arbitrary BL.ByteString where+    arbitrary = BL.pack <$> arbitrary+    shrink bs = BL.pack <$> shrink (BL.unpack bs)++main :: IO ()+main = hspec suite++suite :: Spec+suite = do+    describe "xxh32 strict" $+        it "hashes known pairs" $ do+            xxh32bs' "" `shouldBe` 0x02cc5d05+            xxh32bs' "Hello World" `shouldBe` 0xb1fd16ee+            xxh32bs' "xxhash is a hashing library" `shouldBe` 0x5e213914++    describe "xxh64 strict" $+        it "hashes known pairs" $ do+            xxh64bs' "" `shouldBe` 0xef46db3751d8e999+            xxh64bs' "Hello World" `shouldBe` 0x6334d20719245bc2+            xxh64bs' "xxhash is a hashing library" `shouldBe` 0xea6cd1701a857e7c++    describe "xxh32 lazy" $+        it "hashes known pairs" $ do+            xxh32bs (BL.fromChunks [""]) `shouldBe` 0x02cc5d05+            xxh32bs (BL.fromChunks ["Hello ", "World"]) `shouldBe` 0xb1fd16ee+            xxh32bs (BL.fromChunks ["xxhash is ", "a hashing ", "library"]) `shouldBe` 0x5e213914++    describe "xxh64 lazy" $+        it "hashes known pairs" $ do+            xxh64bs (BL.fromChunks [""]) `shouldBe` 0xef46db3751d8e999+            xxh64bs (BL.fromChunks ["Hello ", "World"]) `shouldBe` 0x6334d20719245bc2+            xxh64bs (BL.fromChunks ["xxhash is ", "a hashing ", "library"]) `shouldBe` 0xea6cd1701a857e7c+    describe "lazy and strict" $+        prop "hashes lazy and strict" $ \(bs :: BL.ByteString) ->+            xxh64 bs 0 == xxh64 (BL.toStrict bs) 0+++xxh32bs' :: ByteString -> Word32+xxh32bs' = flip xxh32 0++xxh64bs' :: ByteString -> Word64+xxh64bs' = flip xxh64 0++xxh32bs :: BL.ByteString -> Word32+xxh32bs = flip xxh32 0++xxh64bs :: BL.ByteString -> Word64+xxh64bs = flip xxh64 0
+ xxhash-ffi.cabal view
@@ -0,0 +1,57 @@+name:                xxhash-ffi+version:             0.1.0.0+synopsis:            Bindings to the C implementation the xxHash algorithm+description:         Bindings to the C implementation the xxHash algorithm. xxHash provides 32-bit and 64-bit extremely fast non-cryptographic hash functions.+homepage:            https://github.com/haskell-haskey/xxhash-ffi#readme+license:             BSD3+license-file:        LICENSE+author:              Henri Verroken+maintainer:          henriverroken@gmail.com+copyright:           2017 Henri Verroken+category:            Cryptography+build-type:          Simple+extra-source-files:  README.md+cabal-version:       >=1.10++library+  hs-source-dirs:      src+  exposed-modules:     Data.Digest.XXHash.FFI+  build-depends:       base >= 4.7 && < 5+                     , bytestring+  ghc-options:         -Wall+  default-language:    Haskell2010+  c-sources:           src-c/xxhash.c+  include-dirs:        src-c+  includes:            xxhash.h++test-suite xxhash-ffi-test+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  build-depends:       base+                     , xxhash-ffi+                     , QuickCheck+                     , bytestring+                     , hspec+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall+  default-language:    Haskell2010++benchmark xxhash-ffi-bench+  type:                exitcode-stdio-1.0+  hs-source-dirs:      bench+  main-is:             Bench.hs+  build-depends:       base+                     , xxhash-ffi+                     , bytestring+                     , criterion+                     , deepseq+                     , digest+                     , hashable+                     , murmur-hash+                     , xxhash+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall+  default-language:    Haskell2010++source-repository head+  type:     git+  location: https://github.com/hverr/xxhash-ffi