-- N.B. this is a little faster than the Haskell code
module ForeignHash ( foreignFileHash ) where

import           Data.Coerce           (coerce)
import           Data.Word             (Word64)
import           Foreign.C.String      (CString, withCString)
import           Foreign.C.Types       (CInt (..), CULLong (..))
import           Foreign.Marshal.Alloc (alloca)
import           Foreign.Ptr           (Ptr)
import           Foreign.Storable      (peek)

foreign import ccall ph_dct_imagehash :: CString -> Ptr CULLong -> IO CInt

-- | Doesn't work with @.gif@ files
--
-- This will throw an exception on failure.
foreignFileHash :: FilePath -> IO Word64
foreignFileHash :: FilePath -> IO Word64
foreignFileHash FilePath
fp = forall a. FilePath -> (CString -> IO a) -> IO a
withCString FilePath
fp forall a b. (a -> b) -> a -> b
$ \CString
cstr ->
    forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca forall a b. (a -> b) -> a -> b
$ \Ptr CULLong
hashPtr -> do
        CInt
res <- CString -> Ptr CULLong -> IO CInt
ph_dct_imagehash CString
cstr Ptr CULLong
hashPtr
        forall {a} {f :: * -> *}. (Eq a, Num a, Applicative f) => a -> f ()
check CInt
res
        coerce :: forall a b. Coercible a b => a -> b
coerce forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Storable a => Ptr a -> IO a
peek Ptr CULLong
hashPtr

    where check :: a -> f ()
check (-1) = forall a. HasCallStack => FilePath -> a
error (FilePath
"Hash of file " forall a. [a] -> [a] -> [a]
++ FilePath
fp forall a. [a] -> [a] -> [a]
++ FilePath
" failed.")
          check a
0    = forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
          check a
_    = forall a. HasCallStack => FilePath -> a
error FilePath
"This should never happen."