{-# LANGUAGE ForeignFunctionInterface, FlexibleInstances #-}
------------------------------------------------------------
-- |
-- Copyright    :   (c) 2008 Eugene Kirpichov
-- License      :   BSD-style
--
-- Maintainer   :   ekirpichov@gmail.com
-- Stability    :   experimental
-- Portability  :   portable (H98 + FFI)
--
-- Adler32 wrapper
--
------------------------------------------------------------

module Data.Digest.Adler32 (
    Adler32, adler32, adler32Update
) where

import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
import Foreign

import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Lazy.Internal as LI
import qualified System.IO.Unsafe as U

#include "zlib.h"

-- | The class of values for which Adler32 may be computed
class Adler32 a where
    -- | Compute Adler32 checksum
    adler32 :: a -> Word32
    adler32 = adler32Update 1

    -- | Given the Adler32 checksum of a string, compute Adler32 of its
    -- concatenation with another string (t.i., incrementally update the 
    -- Adler32 hash value).
    adler32Update :: Word32 -> a -> Word32

instance Adler32 S.ByteString where
    adler32Update = adler32_s_update

instance Adler32 L.ByteString where
    adler32Update = adler32_l_update

instance Adler32 [Word8] where
    adler32Update n = (adler32Update n) . L.pack


adler32_s_update :: Word32 -> S.ByteString -> Word32
adler32_s_update seed str
    | S.null str = seed
    | otherwise =
        U.unsafePerformIO $
        unsafeUseAsCStringLen str $
        \(buf, len) -> fmap fromIntegral $
            adler32_c (fromIntegral seed) (castPtr buf) (fromIntegral len)

adler32_l_update :: Word32 -> L.ByteString -> Word32
adler32_l_update = LI.foldlChunks adler32_s_update


foreign import ccall unsafe "zlib.h adler32"
    adler32_c :: #{type uLong}
              -> Ptr #{type Bytef}
              -> #{type uInt}
              -> IO #{type uLong}
