Dish (empty) → 0.0.0.1
raw patch · 5 files changed
+527/−0 lines, 5 filesdep +basedep +bytestringsetup-changed
Dependencies added: base, bytestring
Files
- Dish.cabal +30/−0
- LICENSE +30/−0
- Setup.hs +2/−0
- cbits/murmur3.c +315/−0
- src/Data/Dish/Murmur3.hs +150/−0
+ Dish.cabal view
@@ -0,0 +1,30 @@+name: Dish+version: 0.0.0.1+homepage: http://github.com/zcourts/Dish+license: BSD3+license-file: LICENSE+author: Courtney Robinson+maintainer: oss@crlog.info+category: Data+build-type: Simple+cabal-version: >=1.8+--extra-source-files: cbits/murmur3.c, cbits/murmur3.h+description: A group of Hash related utilities (currently wraps MurmurHash3 C implementation)+synopsis: Hash modules (currently Murmur3)++source-repository head+ type: git+ location: https://github.com/zcourts/Dish.git++library+ build-depends: + base >=4.6.0 && <4.7,+ bytestring >=0.10.0 && <0.11+ hs-source-dirs: src+ c-sources: cbits/murmur3.c+ include-dirs: cbits+ extra-libraries: + --cc-options: -O3+ extensions: ForeignFunctionInterface+ other-modules: + exposed-modules: Data.Dish.Murmur3
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Courtney Robinson++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 Courtney Robinson 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ cbits/murmur3.c view
@@ -0,0 +1,315 @@+//----------------------------------------------------------------------------- +// MurmurHash3 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. + +// Note - The x86 and x64 versions do _not_ produce the same results, as the +// algorithms are optimized for their respective platforms. You can still +// compile and run any of them on any platform, but your performance with the +// non-native version will be less than optimal. + +#include "murmur3.h" + +//----------------------------------------------------------------------------- +// Platform-specific functions and macros + +#ifdef __GNUC__ +#define FORCE_INLINE __attribute__((always_inline)) inline +#else +#define FORCE_INLINE +#endif + +static inline FORCE_INLINE uint32_t rotl32 ( uint32_t x, int8_t r ) +{ + return (x << r) | (x >> (32 - r)); +} + +static inline FORCE_INLINE uint64_t rotl64 ( uint64_t x, int8_t r ) +{ + return (x << r) | (x >> (64 - r)); +} + +#define ROTL32(x,y) rotl32(x,y) +#define ROTL64(x,y) rotl64(x,y) + +#define BIG_CONSTANT(x) (x##LLU) + +//----------------------------------------------------------------------------- +// Block read - if your platform needs to do endian-swapping or can only +// handle aligned reads, do the conversion here + +#define getblock(p, i) (p[i]) + +//----------------------------------------------------------------------------- +// Finalization mix - force all bits of a hash block to avalanche + +static inline FORCE_INLINE uint32_t fmix32 ( uint32_t h ) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +//---------- + +static inline FORCE_INLINE uint64_t fmix64 ( uint64_t k ) +{ + k ^= k >> 33; + k *= BIG_CONSTANT(0xff51afd7ed558ccd); + k ^= k >> 33; + k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53); + k ^= k >> 33; + + return k; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_32 ( const void * key, int len, + uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 4; + int i; + + uint32_t h1 = seed; + + uint32_t c1 = 0xcc9e2d51; + uint32_t c2 = 0x1b873593; + + //---------- + // body + + const uint32_t * blocks = (const uint32_t *)(data + nblocks*4); + + for(i = -nblocks; i; i++) + { + uint32_t k1 = getblock(blocks,i); + + k1 *= c1; + k1 = ROTL32(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTL32(h1,13); + h1 = h1*5+0xe6546b64; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*4); + + uint32_t k1 = 0; + + switch(len & 3) + { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; + + h1 = fmix32(h1); + + *(uint32_t*)out = h1; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_128 ( const void * key, const int len, + uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 16; + int i; + + uint32_t h1 = seed; + uint32_t h2 = seed; + uint32_t h3 = seed; + uint32_t h4 = seed; + + uint32_t c1 = 0x239b961b; + uint32_t c2 = 0xab0e9789; + uint32_t c3 = 0x38b34ae5; + uint32_t c4 = 0xa1e38b93; + + //---------- + // body + + const uint32_t * blocks = (const uint32_t *)(data + nblocks*16); + + for(i = -nblocks; i; i++) + { + uint32_t k1 = getblock(blocks,i*4+0); + uint32_t k2 = getblock(blocks,i*4+1); + uint32_t k3 = getblock(blocks,i*4+2); + uint32_t k4 = getblock(blocks,i*4+3); + + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + + h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b; + + k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; + + h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747; + + k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; + + h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35; + + k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; + + h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*16); + + uint32_t k1 = 0; + uint32_t k2 = 0; + uint32_t k3 = 0; + uint32_t k4 = 0; + + switch(len & 15) + { + case 15: k4 ^= tail[14] << 16; + case 14: k4 ^= tail[13] << 8; + case 13: k4 ^= tail[12] << 0; + k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; + + case 12: k3 ^= tail[11] << 24; + case 11: k3 ^= tail[10] << 16; + case 10: k3 ^= tail[ 9] << 8; + case 9: k3 ^= tail[ 8] << 0; + k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; + + case 8: k2 ^= tail[ 7] << 24; + case 7: k2 ^= tail[ 6] << 16; + case 6: k2 ^= tail[ 5] << 8; + case 5: k2 ^= tail[ 4] << 0; + k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; + + case 4: k1 ^= tail[ 3] << 24; + case 3: k1 ^= tail[ 2] << 16; + case 2: k1 ^= tail[ 1] << 8; + case 1: k1 ^= tail[ 0] << 0; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; + + h1 += h2; h1 += h3; h1 += h4; + h2 += h1; h3 += h1; h4 += h1; + + h1 = fmix32(h1); + h2 = fmix32(h2); + h3 = fmix32(h3); + h4 = fmix32(h4); + + h1 += h2; h1 += h3; h1 += h4; + h2 += h1; h3 += h1; h4 += h1; + + ((uint32_t*)out)[0] = h1; + ((uint32_t*)out)[1] = h2; + ((uint32_t*)out)[2] = h3; + ((uint32_t*)out)[3] = h4; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x64_128 ( const void * key, const int len, + const uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 16; + int i; + + uint64_t h1 = seed; + uint64_t h2 = seed; + + uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5); + uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f); + + //---------- + // body + + const uint64_t * blocks = (const uint64_t *)(data); + + for(i = 0; i < nblocks; i++) + { + uint64_t k1 = getblock(blocks,i*2+0); + uint64_t k2 = getblock(blocks,i*2+1); + + k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; + + h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729; + + k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; + + h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*16); + + uint64_t k1 = 0; + uint64_t k2 = 0; + + switch(len & 15) + { + case 15: k2 ^= (uint64_t)(tail[14]) << 48; + case 14: k2 ^= (uint64_t)(tail[13]) << 40; + case 13: k2 ^= (uint64_t)(tail[12]) << 32; + case 12: k2 ^= (uint64_t)(tail[11]) << 24; + case 11: k2 ^= (uint64_t)(tail[10]) << 16; + case 10: k2 ^= (uint64_t)(tail[ 9]) << 8; + case 9: k2 ^= (uint64_t)(tail[ 8]) << 0; + k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; + + case 8: k1 ^= (uint64_t)(tail[ 7]) << 56; + case 7: k1 ^= (uint64_t)(tail[ 6]) << 48; + case 6: k1 ^= (uint64_t)(tail[ 5]) << 40; + case 5: k1 ^= (uint64_t)(tail[ 4]) << 32; + case 4: k1 ^= (uint64_t)(tail[ 3]) << 24; + case 3: k1 ^= (uint64_t)(tail[ 2]) << 16; + case 2: k1 ^= (uint64_t)(tail[ 1]) << 8; + case 1: k1 ^= (uint64_t)(tail[ 0]) << 0; + k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; h2 ^= len; + + h1 += h2; + h2 += h1; + + h1 = fmix64(h1); + h2 = fmix64(h2); + + h1 += h2; + h2 += h1; + + ((uint64_t*)out)[0] = h1; + ((uint64_t*)out)[1] = h2; +} + +//----------------------------------------------------------------------------- +
+ src/Data/Dish/Murmur3.hs view
@@ -0,0 +1,150 @@+{-# LANGUAGE ForeignFunctionInterface #-}+{- | +This is a Haskell wrapper around the C port of MurmurHash3.+<https://github.com/zcourts/murmur3>+MurmurHash3 is available at +<https://code.google.com/p/smhasher/wiki/MurmurHash3>+The hash functions are designed to work efficiently on x86 processors; +in particular, they make some assumptions about the endianness of the processor, +and about the speed of unaligned reads. +-} +module Data.Dish.Murmur3(+ -- * Default/Direct Map+ -- $default+ murmur3,+ -- * X86 32 bits+ -- $32+ murmur3Int,+ -- * X86 128 bits+ -- $x128+ murmur3IntegerX86,+ -- * X64 128 bits+ -- $64_128+ murmur3IntegerX64,+ -- * FFI+ murmur3Raw,+ -- * Unsafe+ unsafeMurmur3Int,+ unsafeMurmur3IntegerX86,+ unsafeMurmur3IntegerX64,+ -- * Murmur3 Hash Version+ MHV(..)+ ) where+import Foreign.C+import Foreign.Ptr+import Foreign+import qualified Data.List as L+import qualified Data.Bits as B+import qualified System.IO.Unsafe as US++-- Murmur Hash version, one of +data MHV = X86_32 | X86_128 | X64_128+++{-$default +Simple, verbose interface for generating hashes+-}+-- | Base function, which allows you to choose which 'MHV' to use +murmur3 :: String -- ^ The string to be hashed+ -> Int -- ^ A seed value for the hash function+ -> MHV -- ^ Which Murmur Hash version to use 'X86_32', 'X86_128' or 'X64_128'+ -> IO [Int] -- ^ returns 4, 32 bit ints, if 'X86_32' is used only the first has a value and the other 3 are 0+murmur3 v s ver = do m <- murmur3Raw v s ver; toArr m+ where + toArr :: [CUInt] -> IO [Int]+ toArr [] = return []+ toArr l = return $ b l [] + where b :: [CUInt] -> [Int] -> [Int]+ b xs l2 = foldl (\ list x -> list ++ [w x] ) l2 xs+ w :: CUInt -> Int+ w = fromIntegral++{-$32+Generate 32 bit hash values+-} +{- | has the lowest throughput, but also the lowest latency. If you're making a +hash table that usually has small keys, this is probably the one you want to use +on 32-bit machines. It has a 32-bit output. -} +murmur3Int :: String -- ^ The string to be hashed+ -> Int -- ^ A seed value for the hash function+ -> IO Int -- ^ 32 bit number generated from the string+murmur3Int val seed = do v <- murmur3Raw val seed X86_32; + -- safe to use L.head, list is never empty even if all vals are 0+ return $ fromIntegral (L.head v)++{-$x128+Generate 128 bit hash values, optimized for 32 bit systems+-}+{- | Generate a 128 bit hash from the given value, this function's implementation + is optimized for 32 bit architectures but works on any.+ Has about 30% higher throughput than 'murmur3Int'. Be warned, though, + that its latency for a single 16-byte key is about 86% longer!+-}+murmur3IntegerX86 :: String -- ^ The string to be hashed+ -> Int -- ^ A seed value for the hash function+ -> IO Integer -- ^ 128 bit number generated from the string+murmur3IntegerX86 val seed = x128 val seed X86_128+ +{-$64_128+Generate 128 bit hash values, optimized for 64 bit systems+-}+{- | Generate a 128 bit hash from the given value, this function's implementation + is optimized for x64 architectures but works on any.+ Its throughput is 250% higher than 'murmur3IntegerX86', but it has roughly + the same latency. +-}+murmur3IntegerX64 :: String -- ^ The string to be hashed+ -> Int -- ^ A seed value for the hash function+ -> IO Integer -- ^ 128 bit number generated from the string+murmur3IntegerX64 val seed = x128 val seed X64_128++foreign import ccall "MurmurHash3_x86_32" c_x86_32+ :: CString -> CInt -> CUInt -> Ptr CUInt -> IO ()++foreign import ccall "MurmurHash3_x86_128" c_x86_128+ :: CString -> CInt -> CUInt -> Ptr CUInt -> IO ()++foreign import ccall "MurmurHash3_x64_128" c_x64_128+ :: CString -> CInt -> CUInt -> Ptr CUInt -> IO ()++-- | all murmur functions use this and manipulate its response to return a different format +murmur3Raw :: String -> Int -> MHV -> IO [CUInt]+murmur3Raw val seed ver = do+ val' <- withCAStringLen val $ \x -> return x+ let cstr = strFromCStr val'+ let strLength = strLFromCStr val'+ outPtr <- mallocArray arrSize+ doHash ver cstr strLength (fromIntegral seed) outPtr+ peekArray arrSize outPtr+ where arrSize = 4+ strFromCStr :: CStringLen -> CString+ strFromCStr = fst+ strLFromCStr :: CStringLen -> CInt+ strLFromCStr i = fromIntegral $ snd i+ --version value size seed out + doHash :: MHV -> CString -> CInt -> CUInt -> Ptr CUInt -> IO()+ doHash X86_32 v s se o = c_x86_32 v s se o+ doHash X86_128 v s se o = c_x86_128 v s se o+ doHash X64_128 v s se o = c_x64_128 v s se o++unsafeMurmur3Int :: String -> Int -> Int+unsafeMurmur3Int val seed = US.unsafePerformIO $ murmur3Int val seed++unsafeMurmur3IntegerX86 :: String -> Int -> Integer+unsafeMurmur3IntegerX86 val seed = US.unsafePerformIO $ murmur3IntegerX86 val seed++unsafeMurmur3IntegerX64 :: String -> Int -> Integer+unsafeMurmur3IntegerX64 val seed = US.unsafePerformIO $ murmur3IntegerX64 val seed++x128 :: String -> Int -> MHV -> IO Integer+x128 val seed ver= do + v <- hash ver + return $ twiddle 0 v + where hash :: MHV -> IO [CUInt]+ hash X86_128 = murmur3Raw val seed X86_128+ hash X64_128 = murmur3Raw val seed X64_128+ hash _ = return []+ twiddle :: Integer -> [CUInt] -> Integer+ twiddle i [] = i+ twiddle i (0:xs) = twiddle i xs -- don't shift when val is 0+ twiddle i (x:xs) = twiddle (B.shift i (B.bitSize x) `B.xor` fromIntegral x) xs