{-# LANGUAGE ForeignFunctionInterface #-} -- | -- Module: Data.Digest.XXHash.FFI -- Copyright: (c) 2017 Henri Verroken -- Licence: BSD3 -- Maintainer: Henri Verroken . 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 #-}