module Data.Digest.Adler32
( Adler32Src(..)
, Adler32
, extractAdler32
, makeAdler32
, adler32SlideL
, adler32SlideR
, adler32AppendByte
, adler32UnAppendByte
, adler32PrependByte
, adler32UnPrependByte
, adler32UnAppend
, adler32UnPrepend
) where
import qualified Data.ByteString as B
import qualified Data.ByteString.Unsafe as B
import qualified Data.ByteString.Lazy as BL
import Data.Word (Word8, Word32)
import Data.Bits (unsafeShiftL, unsafeShiftR, (.|.), (.&.))
import Data.Semigroup (Semigroup(..))
#ifdef USE_ZLIB
import qualified Foreign as F
import qualified Foreign.C as F
import qualified System.IO.Unsafe as U
#endif
class Adler32Src a where
adler32 :: a -> Word32
adler32 = extractAdler32 . adler32'
adler32Update :: Word32 -> a -> Word32
adler32Update c s =
extractAdler32 $ makeAdler32 c (0 :: Word32) <> adler32' s
adler32' :: a -> Adler32
adler32' = adler32Update' mempty
adler32Update' :: Adler32 -> a -> Adler32
data Adler32 =
Adler32 !Word32 !Word32 !Word32
deriving (Eq, Ord)
instance Show Adler32 where
show c@(Adler32 _ _ l) =
"makeAdler32 " ++ show (extractAdler32 c) ++ " " ++ show l
extractAdler32 :: Adler32 -> Word32
extractAdler32 (Adler32 a b _) = a .|. (b `unsafeShiftL` 16)
makeAdler32 :: Integral a => Word32 -> a -> Adler32
makeAdler32 c l =
Adler32 (mod0 $ c .&. 0xffff) (mod0 $ c `unsafeShiftR` 16) (fromIntegral $ l `mod` base)
#ifdef USE_ZLIB
foreign import ccall unsafe "adler32"
zlib_adler32 :: F.Word32 -> F.Ptr a -> F.CUInt -> F.Word32
#else
runAdler32 :: B.ByteString -> Word32 -> Word32 -> Word32 -> Adler32
runAdler32 s a0 b0 l0 = loop a0 b0 0 (min nmax len)
where
loop !a !b !i !j
| i < j = loop a' (b + a') (i + 1) j
| j < len = loop (mod1 a) (mod1 b) i (min (i + nmax) len)
| otherwise = Adler32 (mod1 a) (mod1 b) (mod1 (l0 + fromIntegral len))
where
a' = a + fromIntegral (B.unsafeIndex s i)
len = B.length s
nmax = 5552
#endif
instance Adler32Src B.ByteString where
#ifndef USE_ZLIB
adler32' s = runAdler32 s 1 0 0
adler32Update' (Adler32 a b l) s =
runAdler32 s a b l
#else
adler32 = adler32Update 1
adler32Update c s =
U.unsafePerformIO $
B.unsafeUseAsCStringLen s $ \(ptr, len) -> do
return $ zlib_adler32 c ptr (fromIntegral len)
adler32' s = makeAdler32 (adler32 s) (B.length s)
adler32Update' c@(Adler32 _ _ l) s =
makeAdler32 (adler32Update (extractAdler32 c) s) (l + fromIntegral (B.length s))
#endif
instance Adler32Src BL.ByteString where
adler32Update' = BL.foldlChunks (\c s -> c <> adler32' s)
instance Semigroup Adler32 where
Adler32 a1 b1 l1 <> Adler32 a2 b2 l2 =
Adler32 (mod0 $ a1m1 + a2) b (mod0 $ l1 + l2)
where
b = mod1 $ b1 + b2 + l2 * a1m1
a1m1 = if a1 == 0 then base 1 else a1 1
instance Monoid Adler32 where
mempty = Adler32 1 0 0
#if !(MIN_VERSION_base(4,11,0))
mappend = (<>)
#endif
adler32SlideL :: Word8 -> Adler32 -> Word8 -> Adler32
adler32SlideL d1 c d2 =
d1 `adler32UnPrependByte` (c `adler32AppendByte` d2)
adler32SlideR :: Word8 -> Adler32 -> Word8 -> Adler32
adler32SlideR d1 c d2 =
(d1 `adler32PrependByte` c) `adler32UnAppendByte` d2
adler32AppendByte :: Adler32 -> Word8 -> Adler32
adler32AppendByte (Adler32 a b l) d =
Adler32 a' (mod0 $ b + a') (mod0 $ l + 1)
where
a' = mod0 $ a + fromIntegral d
adler32UnAppendByte :: Adler32 -> Word8 -> Adler32
adler32UnAppendByte (Adler32 a b l) d =
Adler32 (modDiff a (fromIntegral d)) (modDiff b a) (modDiff l 1)
adler32PrependByte :: Word8 -> Adler32 -> Adler32
adler32PrependByte d (Adler32 a b l) =
Adler32 (mod0 $ a + fromIntegral d) (mod1 $ b + l' * fromIntegral d + 1) l'
where
l' = mod0 $ l + 1
adler32UnPrependByte :: Word8 -> Adler32 -> Adler32
adler32UnPrependByte d (Adler32 a b l) =
Adler32 (modDiff a (fromIntegral d)) (modDiff b (mod1 $ l * fromIntegral d + 1)) (modDiff l 1)
adler32UnAppend :: Adler32 -> Adler32 -> Adler32
adler32UnAppend (Adler32 a b l) (Adler32 a2 b2 l2) =
Adler32 a1 b1 (modDiff l l2)
where
b1 = modDiff b (mod1 $ b2 + l2 * a1m1)
a1m1 = if a1 == 0 then base 1 else a1 1
a1 = mod0 $ modDiff a a2 + 1
adler32UnPrepend :: Adler32 -> Adler32 -> Adler32
adler32UnPrepend (Adler32 a1 b1 l1) (Adler32 a b l) =
Adler32 (modDiff a a1m1) b2 l2
where
b2 = modDiff b (mod1 $ b1 + l2 * a1m1)
a1m1 = if a1 == 0 then base 1 else a1 1
l2 = modDiff l l1
mod0 :: Integral a => a -> a
mod0 x
| x < base = x
| otherwise = x base
mod1 :: Integral a => a -> a
mod1 x = x `rem` base
modDiff :: Integral a => a -> a -> a
modDiff x y
| x >= y = x y
| otherwise = (x + base) y
base :: Num a => a
base = 65521