ghc-internal-9.1001.0: src/GHC/Internal/Fingerprint.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE Trustworthy #-}
-- ----------------------------------------------------------------------------
--
-- (c) The University of Glasgow 2006
--
-- Fingerprints for recompilation checking and ABI versioning, and
-- implementing fast comparison of Typeable.
--
-- ----------------------------------------------------------------------------
module GHC.Internal.Fingerprint (
Fingerprint(..), fingerprint0,
fingerprintData,
fingerprintString,
fingerprintFingerprints,
getFileHash
) where
import GHC.Internal.IO
import GHC.Internal.Base
import GHC.Internal.Bits
import GHC.Internal.Num
import GHC.Internal.List
import GHC.Internal.Real
import GHC.Internal.Word
import GHC.Internal.Show
import GHC.Internal.Ptr
import GHC.Internal.Foreign.C.Types
import GHC.Internal.Foreign.Marshal.Alloc
import GHC.Internal.Foreign.Marshal.Array
import GHC.Internal.Foreign.Storable
import GHC.Internal.System.IO
import GHC.Internal.Fingerprint.Type
-- for SIZEOF_STRUCT_MD5CONTEXT:
#include "HsBaseConfig.h"
-- XXX instance Storable Fingerprint
-- defined in GHC.Internal.Foreign.Storable to avoid orphan instance
fingerprint0 :: Fingerprint
fingerprint0 = Fingerprint 0 0
fingerprintFingerprints :: [Fingerprint] -> Fingerprint
fingerprintFingerprints fs = unsafeDupablePerformIO $
withArrayLen fs $ \len p ->
fingerprintData (castPtr p) (len * sizeOf (undefined :: Fingerprint))
fingerprintData :: Ptr Word8 -> Int -> IO Fingerprint
fingerprintData buf len =
allocaBytes SIZEOF_STRUCT_MD5CONTEXT $ \pctxt -> do
c_MD5Init pctxt
c_MD5Update pctxt buf (fromIntegral len)
allocaBytes 16 $ \pdigest -> do
c_MD5Final pdigest pctxt
peek (castPtr pdigest :: Ptr Fingerprint)
fingerprintString :: String -> Fingerprint
fingerprintString str = unsafeDupablePerformIO $
withArrayLen word8s $ \len p ->
fingerprintData p len
where word8s = concatMap f str
f c = let w32 :: Word32
w32 = fromIntegral (ord c)
in [fromIntegral (w32 `shiftR` 24),
fromIntegral (w32 `shiftR` 16),
fromIntegral (w32 `shiftR` 8),
fromIntegral w32]
-- | Computes the hash of a given file.
-- This function loops over the handle, running in constant memory.
--
-- @since base-4.7.0.0
getFileHash :: FilePath -> IO Fingerprint
getFileHash path = withBinaryFile path ReadMode $ \h ->
allocaBytes SIZEOF_STRUCT_MD5CONTEXT $ \pctxt -> do
c_MD5Init pctxt
processChunks h (\buf size -> c_MD5Update pctxt buf (fromIntegral size))
allocaBytes 16 $ \pdigest -> do
c_MD5Final pdigest pctxt
peek (castPtr pdigest :: Ptr Fingerprint)
where
_BUFSIZE = 4096
-- Loop over _BUFSIZE sized chunks read from the handle,
-- passing the callback a block of bytes and its size.
processChunks :: Handle -> (Ptr Word8 -> Int -> IO ()) -> IO ()
processChunks h f = allocaBytes _BUFSIZE $ \arrPtr ->
let loop = do
count <- hGetBuf h arrPtr _BUFSIZE
eof <- hIsEOF h
when (count /= _BUFSIZE && not eof) $ errorWithoutStackTrace $
"GHC.Internal.Fingerprint.getFileHash: only read " ++ show count ++ " bytes"
f arrPtr count
when (not eof) loop
in loop
data MD5Context
foreign import ccall unsafe "__hsbase_MD5Init"
c_MD5Init :: Ptr MD5Context -> IO ()
foreign import ccall unsafe "__hsbase_MD5Update"
c_MD5Update :: Ptr MD5Context -> Ptr Word8 -> CInt -> IO ()
foreign import ccall unsafe "__hsbase_MD5Final"
c_MD5Final :: Ptr Word8 -> Ptr MD5Context -> IO ()