%
% (c) The GRASP/AQUA Project, Glasgow University, 1998
%
\section[md5]{MD5: Message-digest}

This module provides basic MD5 support for Haskell, using
Colin Plumb's C implementation of MD5 to do the Hard Work.

\begin{code}
{-# OPTIONS -fglasgow-exts -#include "cbits/HsUtil.h" #-}
module MD5 
	(
          digest	-- :: String	   -> IO String
	, digestPS      -- :: PackedString -> IO (ByteArray Int)
	) where

import Monad		( liftM )
import GHC.Ptr		( Ptr(..) )
import Foreign		( allocaBytes )
import Addr             ( Addr(..) )
import ST		( stToIO )
import ByteArray	( ByteArray, indexCharArray )
import PackedString
\end{code}

\begin{code}
digest :: String -> IO String
digest str = do
  ba <- digestPS =<< stToIO (packStringST str)
  return $ map (indexCharArray ba) [0 .. 15]

digestPS :: PackedString -> IO (ByteArray Int)
digestPS ps =
  -- HACK: needs to be synched with sizeof(struct MD5Context)  
  allocaBytes ((4 + 2 + 16) * 4) $ \ctxt ->
  allocaBytes 16 $ \dig@(Ptr digAddr) -> do
  md5init ctxt
  -- Efficiency hack to avoid copying
  (if isCString ps
      then md5update   ctxt (case psToCString ps of A# a -> Ptr a)
      else md5updateBA ctxt (psToByteArray ps)) (lengthPS ps)
  md5final dig ctxt
  liftM psToByteArray $ stToIO (packCBytesST 16 (A# digAddr))

-- TODO: Use Storable & friends
type MD5Context = Ptr ()
type Digest     = Ptr ()

foreign import "__hslibs_MD5Init"   unsafe md5init     :: MD5Context                       -> IO ()
foreign import "__hslibs_MD5Update" unsafe md5update   :: MD5Context -> Ptr ()      -> Int -> IO ()
foreign import "__hslibs_MD5Update" unsafe md5updateBA :: MD5Context -> ByteArray a -> Int -> IO ()
foreign import "__hslibs_MD5Final"  unsafe md5final    :: Digest     -> MD5Context         -> IO ()

\end{code}
