| 1 | -- ---------------------------------------------------------------------------- |
|---|
| 2 | -- |
|---|
| 3 | -- (c) The University of Glasgow 2006 |
|---|
| 4 | -- |
|---|
| 5 | -- Fingerprints for recompilation checking and ABI versioning. |
|---|
| 6 | -- |
|---|
| 7 | -- http://hackage.haskell.org/trac/ghc/wiki/Commentary/Compiler/RecompilationAvoidance |
|---|
| 8 | -- |
|---|
| 9 | -- ---------------------------------------------------------------------------- |
|---|
| 10 | |
|---|
| 11 | module Fingerprint ( |
|---|
| 12 | Fingerprint(..), fingerprint0, |
|---|
| 13 | readHexFingerprint, |
|---|
| 14 | fingerprintData, |
|---|
| 15 | fingerprintString |
|---|
| 16 | ) where |
|---|
| 17 | |
|---|
| 18 | #include "md5.h" |
|---|
| 19 | ##include "HsVersions.h" |
|---|
| 20 | |
|---|
| 21 | import Outputable |
|---|
| 22 | |
|---|
| 23 | import Text.Printf |
|---|
| 24 | import Numeric ( readHex ) |
|---|
| 25 | |
|---|
| 26 | ##if __GLASGOW_HASKELL__ >= 701 |
|---|
| 27 | -- The MD5 implementation is now in base, to support Typeable |
|---|
| 28 | import GHC.Fingerprint |
|---|
| 29 | ##endif |
|---|
| 30 | |
|---|
| 31 | ##if __GLASGOW_HASKELL__ < 701 |
|---|
| 32 | import Data.Char |
|---|
| 33 | import Foreign |
|---|
| 34 | import Foreign.C |
|---|
| 35 | import GHC.IO (unsafeDupablePerformIO) |
|---|
| 36 | |
|---|
| 37 | -- Using 128-bit MD5 fingerprints for now. |
|---|
| 38 | |
|---|
| 39 | data Fingerprint = Fingerprint {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64 |
|---|
| 40 | deriving (Eq, Ord) |
|---|
| 41 | -- or ByteString? |
|---|
| 42 | |
|---|
| 43 | fingerprint0 :: Fingerprint |
|---|
| 44 | fingerprint0 = Fingerprint 0 0 |
|---|
| 45 | |
|---|
| 46 | peekFingerprint :: Ptr Word8 -> IO Fingerprint |
|---|
| 47 | peekFingerprint p = do |
|---|
| 48 | let peekW64 :: Ptr Word8 -> Int -> Word64 -> IO Word64 |
|---|
| 49 | STRICT3(peekW64) |
|---|
| 50 | peekW64 _ 0 i = return i |
|---|
| 51 | peekW64 p n i = do |
|---|
| 52 | w8 <- peek p |
|---|
| 53 | peekW64 (p `plusPtr` 1) (n-1) |
|---|
| 54 | ((i `shiftL` 8) .|. fromIntegral w8) |
|---|
| 55 | |
|---|
| 56 | high <- peekW64 p 8 0 |
|---|
| 57 | low <- peekW64 (p `plusPtr` 8) 8 0 |
|---|
| 58 | return (Fingerprint high low) |
|---|
| 59 | |
|---|
| 60 | fingerprintData :: Ptr Word8 -> Int -> IO Fingerprint |
|---|
| 61 | fingerprintData buf len = do |
|---|
| 62 | allocaBytes (#const sizeof(struct MD5Context)) $ \pctxt -> do |
|---|
| 63 | c_MD5Init pctxt |
|---|
| 64 | c_MD5Update pctxt buf (fromIntegral len) |
|---|
| 65 | allocaBytes 16 $ \pdigest -> do |
|---|
| 66 | c_MD5Final pdigest pctxt |
|---|
| 67 | peekFingerprint (castPtr pdigest) |
|---|
| 68 | |
|---|
| 69 | -- This is duplicated in libraries/base/GHC/Fingerprint.hs |
|---|
| 70 | fingerprintString :: String -> Fingerprint |
|---|
| 71 | fingerprintString str = unsafeDupablePerformIO $ |
|---|
| 72 | withArrayLen word8s $ \len p -> |
|---|
| 73 | fingerprintData p len |
|---|
| 74 | where word8s = concatMap f str |
|---|
| 75 | f c = let w32 :: Word32 |
|---|
| 76 | w32 = fromIntegral (ord c) |
|---|
| 77 | in [fromIntegral (w32 `shiftR` 24), |
|---|
| 78 | fromIntegral (w32 `shiftR` 16), |
|---|
| 79 | fromIntegral (w32 `shiftR` 8), |
|---|
| 80 | fromIntegral w32] |
|---|
| 81 | |
|---|
| 82 | data MD5Context |
|---|
| 83 | |
|---|
| 84 | foreign import ccall unsafe "MD5Init" |
|---|
| 85 | c_MD5Init :: Ptr MD5Context -> IO () |
|---|
| 86 | foreign import ccall unsafe "MD5Update" |
|---|
| 87 | c_MD5Update :: Ptr MD5Context -> Ptr Word8 -> CInt -> IO () |
|---|
| 88 | foreign import ccall unsafe "MD5Final" |
|---|
| 89 | c_MD5Final :: Ptr Word8 -> Ptr MD5Context -> IO () |
|---|
| 90 | ##endif |
|---|
| 91 | |
|---|
| 92 | instance Outputable Fingerprint where |
|---|
| 93 | ppr (Fingerprint w1 w2) = text (printf "%016x%016x" i1 i2) |
|---|
| 94 | where i1 = fromIntegral w1 :: Integer |
|---|
| 95 | i2 = fromIntegral w2 :: Integer |
|---|
| 96 | -- printf in GHC 6.4.2 didn't have Word64 instances |
|---|
| 97 | |
|---|
| 98 | -- useful for parsing the output of 'md5sum', should we want to do that. |
|---|
| 99 | readHexFingerprint :: String -> Fingerprint |
|---|
| 100 | readHexFingerprint s = Fingerprint w1 w2 |
|---|
| 101 | where (s1,s2) = splitAt 16 s |
|---|
| 102 | [(w1,"")] = readHex s1 |
|---|
| 103 | [(w2,"")] = readHex (take 16 s2) |
|---|