packages feed

pure-zlib 0.6.8 → 0.8.0

raw patch · 13 files changed

+959/−1384 lines, 13 filesdep +ghc-primdep +primitivedep +vectorsetup-changedPVP ok

version bump matches the API change (PVP)

Dependencies added: ghc-prim, primitive, vector, zlib

API changes (from Hackage documentation)

- Codec.Compression.Zlib.Monad: instance GHC.Base.Applicative Codec.Compression.Zlib.Monad.DeflateM
- Codec.Compression.Zlib.Monad: instance GHC.Base.Functor Codec.Compression.Zlib.Monad.DeflateM
- Codec.Compression.Zlib.Monad: instance GHC.Base.Monad Codec.Compression.Zlib.Monad.DeflateM
- Codec.Compression.Zlib.Monad: instance GHC.Show.Show Codec.Compression.Zlib.Monad.DecompressionState
+ Codec.Compression.Zlib.Monad: instance GHC.Base.Applicative (Codec.Compression.Zlib.Monad.DeflateM s)
+ Codec.Compression.Zlib.Monad: instance GHC.Base.Functor (Codec.Compression.Zlib.Monad.DeflateM s)
+ Codec.Compression.Zlib.Monad: instance GHC.Base.Monad (Codec.Compression.Zlib.Monad.DeflateM s)
+ Codec.Compression.Zlib.Monad: instance GHC.Show.Show (Codec.Compression.Zlib.Monad.DecompressionState s)
- Codec.Compression.Zlib: Chunk :: ByteString -> ZlibDecoder -> ZlibDecoder
+ Codec.Compression.Zlib: Chunk :: ByteString -> ST s (ZlibDecoder s) -> ZlibDecoder s
- Codec.Compression.Zlib: DecompError :: DecompressionError -> ZlibDecoder
+ Codec.Compression.Zlib: DecompError :: DecompressionError -> ZlibDecoder s
- Codec.Compression.Zlib: Done :: ZlibDecoder
+ Codec.Compression.Zlib: Done :: ZlibDecoder s
- Codec.Compression.Zlib: NeedMore :: (ByteString -> ZlibDecoder) -> ZlibDecoder
+ Codec.Compression.Zlib: NeedMore :: (ByteString -> ST s (ZlibDecoder s)) -> ZlibDecoder s
- Codec.Compression.Zlib: data ZlibDecoder
+ Codec.Compression.Zlib: data ZlibDecoder s
- Codec.Compression.Zlib: decompressIncremental :: ZlibDecoder
+ Codec.Compression.Zlib: decompressIncremental :: ST s (ZlibDecoder s)
- Codec.Compression.Zlib.Deflate: inflate :: DeflateM ()
+ Codec.Compression.Zlib.Deflate: inflate :: DeflateM s ()
- Codec.Compression.Zlib.Monad: Chunk :: ByteString -> ZlibDecoder -> ZlibDecoder
+ Codec.Compression.Zlib.Monad: Chunk :: ByteString -> ST s (ZlibDecoder s) -> ZlibDecoder s
- Codec.Compression.Zlib.Monad: DecompError :: DecompressionError -> ZlibDecoder
+ Codec.Compression.Zlib.Monad: DecompError :: DecompressionError -> ZlibDecoder s
- Codec.Compression.Zlib.Monad: Done :: ZlibDecoder
+ Codec.Compression.Zlib.Monad: Done :: ZlibDecoder s
- Codec.Compression.Zlib.Monad: NeedMore :: (ByteString -> ZlibDecoder) -> ZlibDecoder
+ Codec.Compression.Zlib.Monad: NeedMore :: (ByteString -> ST s (ZlibDecoder s)) -> ZlibDecoder s
- Codec.Compression.Zlib.Monad: advanceToByte :: DeflateM ()
+ Codec.Compression.Zlib.Monad: advanceToByte :: DeflateM s ()
- Codec.Compression.Zlib.Monad: data DeflateM a
+ Codec.Compression.Zlib.Monad: data DeflateM s a
- Codec.Compression.Zlib.Monad: data ZlibDecoder
+ Codec.Compression.Zlib.Monad: data ZlibDecoder s
- Codec.Compression.Zlib.Monad: emitBlock :: ByteString -> DeflateM ()
+ Codec.Compression.Zlib.Monad: emitBlock :: ByteString -> DeflateM s ()
- Codec.Compression.Zlib.Monad: emitByte :: Word8 -> DeflateM ()
+ Codec.Compression.Zlib.Monad: emitByte :: Word8 -> DeflateM s ()
- Codec.Compression.Zlib.Monad: emitPastChunk :: Int -> Int64 -> DeflateM ()
+ Codec.Compression.Zlib.Monad: emitPastChunk :: Int -> Int -> DeflateM s ()
- Codec.Compression.Zlib.Monad: finalAdler :: DeflateM Word32
+ Codec.Compression.Zlib.Monad: finalAdler :: DeflateM s Word32
- Codec.Compression.Zlib.Monad: finalize :: DeflateM ()
+ Codec.Compression.Zlib.Monad: finalize :: DeflateM s ()
- Codec.Compression.Zlib.Monad: moveWindow :: DeflateM ()
+ Codec.Compression.Zlib.Monad: moveWindow :: DeflateM s ()
- Codec.Compression.Zlib.Monad: nextBits :: (Num a, Bits a) => Int -> DeflateM a
+ Codec.Compression.Zlib.Monad: nextBits :: (Num a, Bits a) => Int -> DeflateM s a
- Codec.Compression.Zlib.Monad: nextBlock :: Integral a => a -> DeflateM ByteString
+ Codec.Compression.Zlib.Monad: nextBlock :: Integral a => a -> DeflateM s ByteString
- Codec.Compression.Zlib.Monad: nextByte :: DeflateM Word8
+ Codec.Compression.Zlib.Monad: nextByte :: DeflateM s Word8
- Codec.Compression.Zlib.Monad: nextCode :: Show a => HuffmanTree a -> DeflateM a
+ Codec.Compression.Zlib.Monad: nextCode :: Show a => HuffmanTree a -> DeflateM s a
- Codec.Compression.Zlib.Monad: nextWord16 :: DeflateM Word16
+ Codec.Compression.Zlib.Monad: nextWord16 :: DeflateM s Word16
- Codec.Compression.Zlib.Monad: nextWord32 :: DeflateM Word32
+ Codec.Compression.Zlib.Monad: nextWord32 :: DeflateM s Word32
- Codec.Compression.Zlib.Monad: raise :: DecompressionError -> DeflateM a
+ Codec.Compression.Zlib.Monad: raise :: DecompressionError -> DeflateM s a
- Codec.Compression.Zlib.Monad: runDeflateM :: DeflateM () -> ZlibDecoder
+ Codec.Compression.Zlib.Monad: runDeflateM :: DeflateM s () -> ST s (ZlibDecoder s)
- Codec.Compression.Zlib.OutputWindow: addByte :: OutputWindow -> Word8 -> OutputWindow
+ Codec.Compression.Zlib.OutputWindow: addByte :: OutputWindow s -> Word8 -> ST s (OutputWindow s)
- Codec.Compression.Zlib.OutputWindow: addChunk :: OutputWindow -> ByteString -> OutputWindow
+ Codec.Compression.Zlib.OutputWindow: addChunk :: OutputWindow s -> ByteString -> ST s (OutputWindow s)
- Codec.Compression.Zlib.OutputWindow: addOldChunk :: OutputWindow -> Int -> Int64 -> (OutputWindow, ByteString)
+ Codec.Compression.Zlib.OutputWindow: addOldChunk :: OutputWindow s -> Int -> Int -> ST s (OutputWindow s, ByteString)
- Codec.Compression.Zlib.OutputWindow: data OutputWindow
+ Codec.Compression.Zlib.OutputWindow: data OutputWindow s
- Codec.Compression.Zlib.OutputWindow: emitExcess :: OutputWindow -> Maybe (ByteString, OutputWindow)
+ Codec.Compression.Zlib.OutputWindow: emitExcess :: OutputWindow s -> ST s (Maybe (ByteString, OutputWindow s))
- Codec.Compression.Zlib.OutputWindow: emptyWindow :: OutputWindow
+ Codec.Compression.Zlib.OutputWindow: emptyWindow :: ST s (OutputWindow s)
- Codec.Compression.Zlib.OutputWindow: finalizeWindow :: OutputWindow -> ByteString
+ Codec.Compression.Zlib.OutputWindow: finalizeWindow :: OutputWindow s -> ST s ByteString

Files

Benchmark.hs view
@@ -1,39 +1,87 @@-import Codec.Compression.Zlib(ZlibDecoder(..), decompressIncremental)-import qualified Data.ByteString      as S+import qualified CZlib+import qualified CZlib.Internal as CZlibIncremental+import qualified PureZlib++import qualified Control.Monad.ST.Lazy as CM+import Criterion.Main+import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L+import qualified GHC.ST as GHC import Prelude hiding (readFile, writeFile)-import Criterion.Main  testCases :: [String]-testCases = [ "randtest1", "randtest2", "randtest3",-              "rfctest1",  "rfctest2",  "rfctest3",-              "zerotest1", "zerotest2", "zerotest3",-              "tor-list" ]+testCases =+  [ "randtest1"+  , "randtest2"+  , "randtest3"+  , "rfctest1"+  , "rfctest2"+  , "rfctest3"+  , "zerotest1"+  , "zerotest2"+  , "zerotest3"+  , "tor-list"+  ]  main :: IO ()-main = defaultMain-  [-    bgroup "decompression" $ flip fmap testCases $ \tc ->-      env (getFiles tc) $ \ ~(zbstr, goldbstr) ->-        bench tc $ whnf (runDecompression (L.toChunks zbstr) goldbstr) decompressIncremental-  ]-  where-    getFiles tc = do-      zbstr    <- L.readFile $ "test/test-cases/" ++ tc ++ ".z"-      goldbstr <- L.readFile $ "test/test-cases/" ++ tc ++ ".gold"-      pure (zbstr, goldbstr)+main =+  defaultMain+    [ bgroup "decompression" $+        flip fmap testCases $+          \tc -> env (getFiles tc) $+            \ ~(zbstr, _) ->+              bgroup+                tc+                [ bgroup+                    "normal"+                    [ bench "pure-zlib" $ whnf PureZlib.decompress zbstr+                    , bench "zlib" $ whnf CZlib.decompress zbstr+                    ]+                , bgroup+                    "incremental"+                    [ bench "pure-zlib" $ whnf decompressIncrementalPure zbstr+                    , bench "zlib" $ whnf decompressIncrementalC zbstr+                    ]+                ]+    ]+ where+  getFiles tc = do+    zbstr <- L.readFile $ "test/test-cases/" ++ tc ++ ".z"+    goldbstr <- L.readFile $ "test/test-cases/" ++ tc ++ ".gold"+    pure (zbstr, goldbstr) -runDecompression :: [S.ByteString] -> L.ByteString -> ZlibDecoder -> ()-runDecompression ls real decoder =-  case decoder of-    Done | not (null ls) -> error "ERROR: Finished decompression with data left."-    Done | not (L.null real) -> error "ERROR: Did not completely decompress file."-    Done | otherwise -> ()-    DecompError e -> error ("ERROR: " ++ show e)-    NeedMore f | (x:rest) <- ls -> runDecompression rest real (f x)-               | otherwise      -> error "ERROR: Ran out of data mid-decompression."-    Chunk c m ->-      let (realfirst, realrest) = L.splitAt (L.length c) real-      in if realfirst == c-           then runDecompression ls realrest m-           else error "Mismatch in decompression"+decompressIncrementalPure :: L.ByteString -> L.ByteString+decompressIncrementalPure input = GHC.runST $ do+  initialState <- PureZlib.decompressIncremental+  go initialState (L.toChunks input) []+ where+  go decoder ls chunks =+    case decoder of+      PureZlib.NeedMore f+        | (x : rest) <- ls -> do+          nextState <- f x+          go nextState rest chunks+        | otherwise -> error "ERROR: Ran out of data mid-decompression."+      PureZlib.Chunk c m -> do+        nextState <- m+        go nextState ls (c : chunks)+      PureZlib.Done | not (null ls) -> error "ERROR: Finished decompression with data left."+      PureZlib.Done | otherwise -> return (L.fromChunks (reverse chunks))+      PureZlib.DecompError e -> error ("ERROR: " ++ show e)++decompressIncrementalC :: L.ByteString -> L.ByteString+decompressIncrementalC input = CM.runST $ go (CZlibIncremental.decompressST CZlibIncremental.zlibFormat CZlibIncremental.defaultDecompressParams) (L.toChunks input) []+ where+  go decoder ls chunks = case decoder of+    CZlibIncremental.DecompressInputRequired f+      | (x : rest) <- ls -> do+        next <- f x+        go next rest chunks+      | otherwise -> error "ERROR: Ran out of data mid-decompression."+    CZlibIncremental.DecompressOutputAvailable c kont -> do+      next <- kont+      go next ls (c : chunks)+    CZlibIncremental.DecompressStreamEnd leftovers+      | not (S.null leftovers) -> error "ERROR: Finished decompression with data left."+      | otherwise -> pure $ L.fromChunks $ reverse chunks+    CZlibIncremental.DecompressStreamError e -> error ("ERROR: " ++ show e)
Deflate.hs view
@@ -1,40 +1,48 @@-import Codec.Compression.Zlib(ZlibDecoder(..), decompressIncremental)-import Control.Monad(unless)-import qualified Data.ByteString      as S+{-# LANGUAGE RankNTypes #-}++import Codec.Compression.Zlib (ZlibDecoder (..), decompressIncremental)+import Control.Monad (unless)+import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L-import Data.List(isSuffixOf)+import Data.List (isSuffixOf)+import GHC.IO (stToIO)+import GHC.Prim (RealWorld)+import GHC.ST (ST)+import System.Environment (getArgs)+import System.IO (Handle, IOMode (..), hClose, openFile) import Prelude hiding (readFile, writeFile)-import System.Environment(getArgs)-import System.IO(IOMode(..), Handle, openFile, hClose)  main :: IO ()-main =-  do args <- getArgs-     case args of-       [ifile] ->-         if ".z" `isSuffixOf` ifile-           then do bstr <- L.readFile ifile-                   let outname = take (length ifile - 2) ifile-                   hndl <- openFile outname WriteMode-                   runDecompression hndl (L.toChunks bstr) decompressIncremental-           else putStrLn "Unexpected file name."-       _ ->-         putStrLn "USAGE: deflate [filename]"+main = do+  args <- getArgs+  case args of+    [ifile] ->+      if ".z" `isSuffixOf` ifile+        then do+          bstr <- L.readFile ifile+          let outname = take (length ifile - 2) ifile+          hndl <- openFile outname WriteMode+          runDecompression hndl (L.toChunks bstr) decompressIncremental+        else putStrLn "Unexpected file name."+    _ ->+      putStrLn "USAGE: deflate [filename]" -runDecompression :: Handle -> [S.ByteString] -> ZlibDecoder -> IO ()-runDecompression hndl ls decoder =-  case decoder of-    Done ->-      do unless (null ls) $-           putStrLn "WARNING: Finished decompression with data left."-         hClose hndl-    DecompError e ->-      do putStrLn ("ERROR: " ++ show e)-         hClose hndl-    NeedMore f | (x:rest) <- ls -> runDecompression hndl rest (f x)-               | otherwise      ->-      do putStrLn "ERROR: Ran out of data mid-decompression."-         hClose hndl-    Chunk c m ->-      do L.hPut hndl c-         runDecompression hndl ls m+runDecompression :: Handle -> [S.ByteString] -> ST RealWorld (ZlibDecoder RealWorld) -> IO ()+runDecompression hndl ls decoder = do+  nextState <- stToIO decoder+  case nextState of+    Done -> do+      unless (null ls) $+        putStrLn "WARNING: Finished decompression with data left."+      hClose hndl+    DecompError e -> do+      putStrLn ("ERROR: " ++ show e)+      hClose hndl+    NeedMore f+      | (x : rest) <- ls -> runDecompression hndl rest (f x)+      | otherwise -> do+        putStrLn "ERROR: Ran out of data mid-decompression."+        hClose hndl+    Chunk c m -> do+      S.hPut hndl c+      runDecompression hndl ls m
Setup.hs view
@@ -1,2 +1,3 @@ import Distribution.Simple+ main = defaultMain
pure-zlib.cabal view
@@ -1,5 +1,6 @@+cabal-version:       2.0 name:                pure-zlib-version:             0.6.8+version:             0.8.0 synopsis:            A Haskell-only implementation of zlib / DEFLATE homepage:            http://github.com/GaloisInc/pure-zlib license:             BSD3@@ -8,12 +9,18 @@ maintainer:          awick@galois.com category:            Codec build-type:          Simple-cabal-version:       1.18 description:         A Haskell-only implementation of the zlib / DEFLATE                      protocol. Currently only implements the decompression                      algorithm. extra-source-files: test/test-cases/*.z,                     test/test-cases/*.gold+tested-with:+  GHC==8.0.2,+  GHC==8.2.2,+  GHC==8.4.4,+  GHC==8.6.5,+  GHC==8.8.4,+  GHC==8.10.4  library   default-language:   Haskell2010@@ -25,7 +32,9 @@                       base-compat        >= 0.9.1 && < 0.12,                       bytestring         >= 0.10  && < 0.11,                       bytestring-builder >= 0.10  && < 0.11,-                      containers         >= 0.5   && < 0.7+                      containers         >= 0.5   && < 0.7,+                      vector,+                      primitive   if !impl(ghc >= 8.0)     build-depends: semigroups == 0.18.*   exposed-modules:@@ -35,8 +44,6 @@                       Codec.Compression.Zlib.HuffmanTree,                       Codec.Compression.Zlib.Monad,                       Codec.Compression.Zlib.OutputWindow-  other-modules:-                      Codec.Compression.Zlib.FingerTree   default-extensions:                       BangPatterns,                       DeriveDataTypeable,@@ -52,6 +59,7 @@                       base        >= 4.6   && < 5.0,                       base-compat >= 0.9.1 && < 0.12,                       bytestring  >= 0.10  && < 0.11,+                      ghc-prim,                       pure-zlib  test-suite test-zlib@@ -84,7 +92,12 @@                       bytestring  >= 0.10  && < 0.11,                       criterion   >= 1.5,                         pure-zlib,+                      zlib,                       time        >= 1.4.2 && < 1.11+  mixins:+    pure-zlib (Codec.Compression.Zlib as PureZlib),+    zlib (Codec.Compression.Zlib as CZlib),+    zlib (Codec.Compression.Zlib.Internal as CZlib.Internal)  source-repository head   type: git
src/Codec/Compression/Zlib.hs view
@@ -1,58 +1,69 @@ {-# LANGUAGE MultiWayIf #-}-module Codec.Compression.Zlib(-         DecompressionError(..)-       , ZlibDecoder(NeedMore, Chunk, Done, DecompError)-       , decompress-       , decompressIncremental-       )- where -import           Codec.Compression.Zlib.Deflate(inflate)-import           Codec.Compression.Zlib.Monad(ZlibDecoder(..), DeflateM,-                                              DecompressionError(..),-                                              runDeflateM, raise, nextByte)-import           Control.Monad(unless, when, replicateM_)-import           Data.Bits((.|.), (.&.), shiftL, shiftR, testBit)-import           Data.ByteString.Builder(lazyByteString,toLazyByteString)+module Codec.Compression.Zlib (+  DecompressionError (..),+  ZlibDecoder (NeedMore, Chunk, Done, DecompError),+  decompress,+  decompressIncremental,+) where++import Codec.Compression.Zlib.Deflate (inflate)+import Codec.Compression.Zlib.Monad (+  DecompressionError (..),+  DeflateM,+  ZlibDecoder (..),+  nextByte,+  raise,+  runDeflateM,+ )+import Control.Monad (replicateM_, unless, when)+import Data.Bits (shiftL, shiftR, testBit, (.&.), (.|.))+import qualified Data.ByteString as S+import Data.ByteString.Builder (Builder, byteString, toLazyByteString) import qualified Data.ByteString.Lazy as L-import           Data.Semigroup ((<>))-import           Data.Word(Word16)-import           Prelude()-import           Prelude.Compat+import Data.Word (Word16)+import GHC.ST (ST, runST)+import Prelude.Compat+import Prelude () -decompressIncremental :: ZlibDecoder+decompressIncremental :: ST s (ZlibDecoder s) decompressIncremental = runDeflateM inflateWithHeaders  decompress :: L.ByteString -> Either DecompressionError L.ByteString-decompress ifile = run decompressIncremental (L.toChunks ifile) mempty+decompress ifile = runST $ do+  base <- decompressIncremental+  run base (L.toChunks ifile) mempty  where+  run :: ZlibDecoder s -> [S.ByteString] -> Builder -> ST s (Either DecompressionError L.ByteString)   run (NeedMore _) [] _ =-    Left (DecompressionError "Ran out of data mid-decompression 2.")-  run (NeedMore f) (first:rest) acc =-    run (f first) rest acc-  run (Chunk c m) ls acc =-    run m ls (acc <> lazyByteString c)-  run Done        [] acc =-    Right (toLazyByteString acc)-  run Done        (_:_) _ =-    Left (DecompressionError "Finished with data remaining.")+    return (Left (DecompressionError "Ran out of data mid-decompression 2."))+  run (NeedMore f) (first : rest) acc = do+    nextState <- f first+    run nextState rest acc+  run (Chunk c m) ls acc = do+    nextState <- m+    run nextState ls (acc <> byteString c)+  run Done [] acc =+    return (Right (toLazyByteString acc))+  run Done (_ : _) _ =+    return (Left (DecompressionError "Finished with data remaining."))   run (DecompError e) _ _ =-    Left e+    return (Left e) -inflateWithHeaders :: DeflateM ()-inflateWithHeaders =-  do cmf <- nextByte-     flg <- nextByte-     let both   = fromIntegral cmf `shiftL` 8 .|. fromIntegral flg-         cm     = cmf .&. 0x0f-         cinfo  = cmf `shiftR` 4-         fdict  = testBit flg 5---       flevel = flg `shiftR` 6-     unless ((both :: Word16) `mod` 31 == 0) $-       raise (HeaderError "Header checksum failed")-     unless (cm == 8) $-       raise (HeaderError ("Bad compression method: " ++ show cm))-     unless (cinfo <= 7) $-       raise (HeaderError ("Window size too big: " ++ show cinfo))-     when fdict $ replicateM_ 4 nextByte -- just skip them for now (FIXME)-     inflate+inflateWithHeaders :: DeflateM s ()+inflateWithHeaders = do+  cmf <- nextByte+  flg <- nextByte+  let both = fromIntegral cmf `shiftL` 8 .|. fromIntegral flg+      cm = cmf .&. 0x0f+      cinfo = cmf `shiftR` 4+      fdict = testBit flg 5+  --       flevel = flg `shiftR` 6+  unless ((both :: Word16) `mod` 31 == 0) $+    raise (HeaderError "Header checksum failed")+  unless (cm == 8) $+    raise (HeaderError ("Bad compression method: " ++ show cm))+  unless (cinfo <= 7) $+    raise (HeaderError ("Window size too big: " ++ show cinfo))+  when fdict $ replicateM_ 4 nextByte -- just skip them for now (FIXME)+  inflate
src/Codec/Compression/Zlib/Adler32.hs view
@@ -1,20 +1,21 @@+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE MagicHash #-}-module Codec.Compression.Zlib.Adler32(-         AdlerState-       , initialAdlerState-       , advanceAdler-       , advanceAdlerBlock-       , finalizeAdler-       )- where -import Data.Bits(shiftL, (.|.))-import qualified Data.ByteString.Lazy as L-import GHC.Exts ( Word#, plusWord#, remWord# )-import GHC.Word ( Word8(..), Word32(..) )+module Codec.Compression.Zlib.Adler32 (+  AdlerState,+  initialAdlerState,+  advanceAdler,+  advanceAdlerBlock,+  finalizeAdler,+) where -data AdlerState = AdlerState { _adlerA :: Word#, _adlerB :: Word# }+import Data.Bits (shiftL, (.|.))+import qualified Data.ByteString as S+import GHC.Exts (Word#, plusWord#, remWord#)+import GHC.Word (Word32 (..), Word8 (..)) +data AdlerState = AdlerState {_adlerA :: Word#, _adlerB :: Word#}+ initialAdlerState :: AdlerState initialAdlerState = AdlerState 1## 0## @@ -33,24 +34,24 @@ {-# INLINE advanceNoMod #-}  -- The block must be less than 5552 bytes long in this case-advanceAdlerLimited :: AdlerState -> L.ByteString -> AdlerState+advanceAdlerLimited :: AdlerState -> S.ByteString -> AdlerState advanceAdlerLimited !state !bl = AdlerState stateA' stateB'  where-  !(AdlerState stateA stateB) = L.foldl' advanceNoMod state bl+  !(AdlerState stateA stateB) = S.foldl' advanceNoMod state bl   stateA' = stateA `remWord#` 65521##   stateB' = stateB `remWord#` 65521## -advanceAdlerBlock :: AdlerState -> L.ByteString -> AdlerState+advanceAdlerBlock :: AdlerState -> S.ByteString -> AdlerState advanceAdlerBlock !state !bl-  | L.length bl == 0 = state-  | L.length bl == 1 = advanceAdler state (L.head bl)-  | L.length bl < 5552 = advanceAdlerLimited state bl +  | S.length bl == 0 = state+  | S.length bl == 1 = advanceAdler state (S.head bl)+  | S.length bl < 5552 = advanceAdlerLimited state bl   | otherwise = advanceAdlerBlock (advanceAdlerBlock state first5551) rest  where-  (!first5551, !rest) = L.splitAt 5551 bl+  (!first5551, !rest) = S.splitAt 5551 bl  finalizeAdler :: AdlerState -> Word32 finalizeAdler (AdlerState a b) = high .|. low-  where-   high = (W32# b) `shiftL` 16-   low = W32# a+ where+  high = (W32# b) `shiftL` 16+  low = W32# a
src/Codec/Compression/Zlib/Deflate.hs view
@@ -1,256 +1,292 @@ {-# LANGUAGE MultiWayIf #-}-module Codec.Compression.Zlib.Deflate(-         inflate-       , computeCodeValues-       )- where -import           Codec.Compression.Zlib.HuffmanTree(HuffmanTree,-                                                    createHuffmanTree)-import           Codec.Compression.Zlib.Monad(DeflateM, DecompressionError(..),-                                              raise,nextBits,nextCode,-                                              nextBlock,nextWord16,nextWord32,-                                              emitByte,emitBlock,emitPastChunk,-                                              advanceToByte, moveWindow,-                                              finalAdler, finalize)-import           Control.Monad(unless, replicateM)-import           Data.Array(Array, array, (!))-import           Data.Bits(shiftL, complement)-import           Data.Int(Int64)-import           Data.List(sortBy)-import           Data.IntMap.Strict(IntMap)+module Codec.Compression.Zlib.Deflate (+  inflate,+  computeCodeValues,+) where++import Codec.Compression.Zlib.HuffmanTree (+  HuffmanTree,+  createHuffmanTree,+ )+import Codec.Compression.Zlib.Monad (+  DecompressionError (..),+  DeflateM,+  advanceToByte,+  emitBlock,+  emitByte,+  emitPastChunk,+  finalAdler,+  finalize,+  moveWindow,+  nextBits,+  nextBlock,+  nextCode,+  nextWord16,+  nextWord32,+  raise,+ )+import Control.Monad (replicateM, unless)+import Data.Array (Array, array, (!))+import Data.Bits (complement, shiftL)+import Data.Int (Int64)+import Data.IntMap.Strict (IntMap) import qualified Data.IntMap.Strict as Map-import           Data.Word(Word8)-import           Numeric(showHex)+import Data.List (sortBy)+import Data.Word (Word8)+import Numeric (showHex) -inflate :: DeflateM ()-inflate =-  do fixedLit  <- buildFixedLitTree-     fixedDist <- buildFixedDistanceTree-     go fixedLit fixedDist+inflate :: DeflateM s ()+inflate = do+  fixedLit <- buildFixedLitTree+  fixedDist <- buildFixedDistanceTree+  go fixedLit fixedDist  where-  go fixedLit fixedDist =-    do isFinal <- inflateBlock fixedLit fixedDist-       moveWindow-       if isFinal-          then checkChecksum >> finalize-          else go fixedLit fixedDist+  go fixedLit fixedDist = do+    isFinal <- inflateBlock fixedLit fixedDist+    moveWindow+    if isFinal+      then checkChecksum >> finalize+      else go fixedLit fixedDist   ---  checkChecksum =-    do advanceToByte-       ourAdler   <- finalAdler-       theirAdler <- nextWord32-       unless (theirAdler == ourAdler) $-         raise (ChecksumError ("checksum mismatch: " ++ showHex theirAdler "" ++-                               " != " ++ showHex ourAdler ""))+  checkChecksum = do+    advanceToByte+    ourAdler <- finalAdler+    theirAdler <- nextWord32+    unless (theirAdler == ourAdler) $+      raise+        ( ChecksumError+            ( "checksum mismatch: " ++ showHex theirAdler ""+                ++ " != "+                ++ showHex ourAdler ""+            )+        ) -inflateBlock :: HuffmanTree Int -> HuffmanTree Int -> DeflateM Bool-inflateBlock fixedLitTree fixedDistanceTree =-  do bfinal <- (== (1::Word8)) `fmap` nextBits 1-     btype  <- nextBits 2-     case btype :: Word8 of-       0 -> -- no compression-         do advanceToByte-            len  <- nextWord16-            nlen <- nextWord16-            unless (len == complement nlen) $-              raise (FormatError "Len/nlen mismatch in uncompressed block.")-            emitBlock =<< nextBlock len-            return bfinal-       1 -> -- compressed with fixed Huffman codes-         do runInflate fixedLitTree fixedDistanceTree-            return bfinal-       2 -> -- compressed with dynamic Huffman codes-         do hlit  <- (257+) `fmap` nextBits 5-            hdist <- (1+)   `fmap` nextBits 5-            hclen <- (4+)   `fmap` nextBits 4-            codeLens <- replicateM hclen (nextBits 3)-            let codeLens' = zip codeLengthOrder codeLens-            codeTree <- computeHuffmanTree codeLens'-            lens <- getCodeLengths codeTree 0 (hlit + hdist) 0 Map.empty-            -- We do this as a big chunk and then split it up because the spec-            -- allows repeat codes to cross the hlit / hdist boundary. So now we-            -- need to pull off the hdist items.-            let (litlens, offdistlens) =-                    Map.partitionWithKey (\ k _ -> k < hlit) lens-                distlens = Map.mapKeys (\ k -> k - hlit) offdistlens-            litTree  <- computeHuffmanTree (Map.toList litlens)-            distTree <- computeHuffmanTree (Map.toList distlens)-            runInflate litTree distTree-            return bfinal-       _ -> -- reserved / error-         raise (FormatError ("Unacceptable BTYPE: " ++ show btype))+inflateBlock :: HuffmanTree Int -> HuffmanTree Int -> DeflateM s Bool+inflateBlock fixedLitTree fixedDistanceTree = do+  bfinal <- (== (1 :: Word8)) `fmap` nextBits 1+  btype <- nextBits 2+  case btype :: Word8 of+    0 -> do+      -- no compression+      advanceToByte+      len <- nextWord16+      nlen <- nextWord16+      unless (len == complement nlen) $+        raise (FormatError "Len/nlen mismatch in uncompressed block.")+      emitBlock =<< nextBlock len+      return bfinal+    1 -> do+      -- compressed with fixed Huffman codes+      runInflate fixedLitTree fixedDistanceTree+      return bfinal+    2 -> do+      -- compressed with dynamic Huffman codes+      hlit <- (257 +) `fmap` nextBits 5+      hdist <- (1 +) `fmap` nextBits 5+      hclen <- (4 +) `fmap` nextBits 4+      codeLens <- replicateM hclen (nextBits 3)+      let codeLens' = zip codeLengthOrder codeLens+      codeTree <- computeHuffmanTree codeLens'+      lens <- getCodeLengths codeTree 0 (hlit + hdist) 0 Map.empty+      -- We do this as a big chunk and then split it up because the spec+      -- allows repeat codes to cross the hlit / hdist boundary. So now we+      -- need to pull off the hdist items.+      let (litlens, offdistlens) =+            Map.partitionWithKey (\k _ -> k < hlit) lens+          distlens = Map.mapKeys (\k -> k - hlit) offdistlens+      litTree <- computeHuffmanTree (Map.toList litlens)+      distTree <- computeHuffmanTree (Map.toList distlens)+      runInflate litTree distTree+      return bfinal+    _ ->+      -- reserved / error+      raise (FormatError ("Unacceptable BTYPE: " ++ show btype))  where-  runInflate :: HuffmanTree Int -> HuffmanTree Int -> DeflateM ()-  runInflate litTree distTree =-    do code <- nextCode litTree-       case compare code 256 of-          LT -> do emitByte (fromIntegral code)-                   runInflate litTree distTree-          EQ -> return ()-          GT -> do len      <- getLength code-                   distCode <- nextCode distTree-                   dist     <- getDistance distCode-                   emitPastChunk dist len-                   runInflate litTree distTree+  runInflate :: HuffmanTree Int -> HuffmanTree Int -> DeflateM s ()+  runInflate litTree distTree = do+    code <- nextCode litTree+    case compare code 256 of+      LT -> do+        emitByte (fromIntegral code)+        runInflate litTree distTree+      EQ -> return ()+      GT -> do+        len <- getLength code+        distCode <- nextCode distTree+        dist <- getDistance distCode+        emitPastChunk dist (fromIntegral len)+        moveWindow+        runInflate litTree distTree  -- ----------------------------------------------------------------------------- -getCodeLengths :: HuffmanTree Int ->-                  Int -> Int -> Int ->-                  IntMap Int ->-                  DeflateM (IntMap Int)+getCodeLengths ::+  HuffmanTree Int ->+  Int ->+  Int ->+  Int ->+  IntMap Int ->+  DeflateM s (IntMap Int) getCodeLengths tree n maxl prev acc-  | n >= maxl   = return acc-  | otherwise =-    do code <- nextCode tree-       if | code <= 15 ->-                getCodeLengths tree (n+1) maxl code (Map.insert n code acc)-          | code == 16 -> -- copy the previous code length 3 - 6 times-             do num <- (3+) `fmap` nextBits 2-                getCodeLengths tree (n+num) maxl prev (addNTimes n num prev acc)-          | code == 17 -> -- repeat a code length of 0 for 3 - 10 times-             do num <- (3+) `fmap` nextBits 3-                getCodeLengths tree (n+num) maxl 0    (addNTimes n num 0 acc)-          | code == 18 -> -- repeat a code length of 0 for 11 - 138 times-             do num <- (11+) `fmap` nextBits 7-                getCodeLengths tree (n+num) maxl 0    (addNTimes n num 0 acc)-          | otherwise ->-             raise (DecompressionError ("Unexpected code: " ++ show code))+  | n >= maxl = return acc+  | otherwise = do+    code <- nextCode tree+    if+        | code <= 15 ->+          getCodeLengths tree (n + 1) maxl code (Map.insert n code acc)+        | code == 16 -> do+          -- copy the previous code length 3 - 6 times+          num <- (3 +) `fmap` nextBits 2+          getCodeLengths tree (n + num) maxl prev (addNTimes n num prev acc)+        | code == 17 -> do+          -- repeat a code length of 0 for 3 - 10 times+          num <- (3 +) `fmap` nextBits 3+          getCodeLengths tree (n + num) maxl 0 (addNTimes n num 0 acc)+        | code == 18 -> do+          -- repeat a code length of 0 for 11 - 138 times+          num <- (11 +) `fmap` nextBits 7+          getCodeLengths tree (n + num) maxl 0 (addNTimes n num 0 acc)+        | otherwise ->+          raise (DecompressionError ("Unexpected code: " ++ show code))  where   addNTimes idx count val old =-    let idxs = take count [idx..]+    let idxs = take count [idx ..]         vals = replicate count val-    in Map.union old (Map.fromList (zip idxs vals))+     in Map.union old (Map.fromList (zip idxs vals))  -- ----------------------------------------------------------------------------- -getLength :: Int -> DeflateM Int64+getLength :: Int -> DeflateM s Int64 getLength c = lengthArray ! c {-# INLINE getLength #-} -lengthArray :: Array Int (DeflateM Int64)-lengthArray = array (257,285) [-    (257, return 3)-  , (258, return 4)-  , (259, return 5)-  , (260, return 6)-  , (261, return 7)-  , (262, return 8)-  , (263, return 9)-  , (264, return 10)-  , (265, (+ 11)  `fmap` nextBits 1)-  , (266, (+ 13)  `fmap` nextBits 1)-  , (267, (+ 15)  `fmap` nextBits 1)-  , (268, (+ 17)  `fmap` nextBits 1)-  , (269, (+ 19)  `fmap` nextBits 2)-  , (270, (+ 23)  `fmap` nextBits 2)-  , (271, (+ 27)  `fmap` nextBits 2)-  , (272, (+ 31)  `fmap` nextBits 2)-  , (273, (+ 35)  `fmap` nextBits 3)-  , (274, (+ 43)  `fmap` nextBits 3)-  , (275, (+ 51)  `fmap` nextBits 3)-  , (276, (+ 59)  `fmap` nextBits 3)-  , (277, (+ 67)  `fmap` nextBits 4)-  , (278, (+ 83)  `fmap` nextBits 4)-  , (279, (+ 99)  `fmap` nextBits 4)-  , (280, (+ 115) `fmap` nextBits 4)-  , (281, (+ 131) `fmap` nextBits 5)-  , (282, (+ 163) `fmap` nextBits 5)-  , (283, (+ 195) `fmap` nextBits 5)-  , (284, (+ 227) `fmap` nextBits 5)-  , (285, return 258)-  ]+lengthArray :: Array Int (DeflateM s Int64)+lengthArray =+  array+    (257, 285)+    [ (257, return 3)+    , (258, return 4)+    , (259, return 5)+    , (260, return 6)+    , (261, return 7)+    , (262, return 8)+    , (263, return 9)+    , (264, return 10)+    , (265, (+ 11) `fmap` nextBits 1)+    , (266, (+ 13) `fmap` nextBits 1)+    , (267, (+ 15) `fmap` nextBits 1)+    , (268, (+ 17) `fmap` nextBits 1)+    , (269, (+ 19) `fmap` nextBits 2)+    , (270, (+ 23) `fmap` nextBits 2)+    , (271, (+ 27) `fmap` nextBits 2)+    , (272, (+ 31) `fmap` nextBits 2)+    , (273, (+ 35) `fmap` nextBits 3)+    , (274, (+ 43) `fmap` nextBits 3)+    , (275, (+ 51) `fmap` nextBits 3)+    , (276, (+ 59) `fmap` nextBits 3)+    , (277, (+ 67) `fmap` nextBits 4)+    , (278, (+ 83) `fmap` nextBits 4)+    , (279, (+ 99) `fmap` nextBits 4)+    , (280, (+ 115) `fmap` nextBits 4)+    , (281, (+ 131) `fmap` nextBits 5)+    , (282, (+ 163) `fmap` nextBits 5)+    , (283, (+ 195) `fmap` nextBits 5)+    , (284, (+ 227) `fmap` nextBits 5)+    , (285, return 258)+    ] -getDistance :: Int -> DeflateM Int+getDistance :: Int -> DeflateM s Int getDistance c = distanceArray ! c {-# INLINE getDistance #-} -distanceArray :: Array Int (DeflateM Int)-distanceArray = array (0,29) [-    (0,  return 1)-  , (1,  return 2)-  , (2,  return 3)-  , (3,  return 4)-  , (4,  (+ 5)     `fmap` nextBits 1)-  , (5,  (+ 7)     `fmap` nextBits 1)-  , (6,  (+ 9)     `fmap` nextBits 2)-  , (7,  (+ 13)    `fmap` nextBits 2)-  , (8,  (+ 17)    `fmap` nextBits 3)-  , (9,  (+ 25)    `fmap` nextBits 3)-  , (10, (+ 33)    `fmap` nextBits 4)-  , (11, (+ 49)    `fmap` nextBits 4)-  , (12, (+ 65)    `fmap` nextBits 5)-  , (13, (+ 97)    `fmap` nextBits 5)-  , (14, (+ 129)   `fmap` nextBits 6)-  , (15, (+ 193)   `fmap` nextBits 6)-  , (16, (+ 257)   `fmap` nextBits 7)-  , (17, (+ 385)   `fmap` nextBits 7)-  , (18, (+ 513)   `fmap` nextBits 8)-  , (19, (+ 769)   `fmap` nextBits 8)-  , (20, (+ 1025)  `fmap` nextBits 9)-  , (21, (+ 1537)  `fmap` nextBits 9)-  , (22, (+ 2049)  `fmap` nextBits 10)-  , (23, (+ 3073)  `fmap` nextBits 10)-  , (24, (+ 4097)  `fmap` nextBits 11)-  , (25, (+ 6145)  `fmap` nextBits 11)-  , (26, (+ 8193)  `fmap` nextBits 12)-  , (27, (+ 12289) `fmap` nextBits 12)-  , (28, (+ 16385) `fmap` nextBits 13)-  , (29, (+ 24577) `fmap` nextBits 13)-  ]+distanceArray :: Array Int (DeflateM s Int)+distanceArray =+  array+    (0, 29)+    [ (0, return 1)+    , (1, return 2)+    , (2, return 3)+    , (3, return 4)+    , (4, (+ 5) `fmap` nextBits 1)+    , (5, (+ 7) `fmap` nextBits 1)+    , (6, (+ 9) `fmap` nextBits 2)+    , (7, (+ 13) `fmap` nextBits 2)+    , (8, (+ 17) `fmap` nextBits 3)+    , (9, (+ 25) `fmap` nextBits 3)+    , (10, (+ 33) `fmap` nextBits 4)+    , (11, (+ 49) `fmap` nextBits 4)+    , (12, (+ 65) `fmap` nextBits 5)+    , (13, (+ 97) `fmap` nextBits 5)+    , (14, (+ 129) `fmap` nextBits 6)+    , (15, (+ 193) `fmap` nextBits 6)+    , (16, (+ 257) `fmap` nextBits 7)+    , (17, (+ 385) `fmap` nextBits 7)+    , (18, (+ 513) `fmap` nextBits 8)+    , (19, (+ 769) `fmap` nextBits 8)+    , (20, (+ 1025) `fmap` nextBits 9)+    , (21, (+ 1537) `fmap` nextBits 9)+    , (22, (+ 2049) `fmap` nextBits 10)+    , (23, (+ 3073) `fmap` nextBits 10)+    , (24, (+ 4097) `fmap` nextBits 11)+    , (25, (+ 6145) `fmap` nextBits 11)+    , (26, (+ 8193) `fmap` nextBits 12)+    , (27, (+ 12289) `fmap` nextBits 12)+    , (28, (+ 16385) `fmap` nextBits 13)+    , (29, (+ 24577) `fmap` nextBits 13)+    ]  -- ----------------------------------------------------------------------------- -buildFixedLitTree :: DeflateM (HuffmanTree Int)-buildFixedLitTree = computeHuffmanTree-  ([(x, 8) | x <- [0   .. 143]] ++-   [(x, 9) | x <- [144 .. 255]] ++-   [(x, 7) | x <- [256 .. 279]] ++-   [(x, 8) | x <- [280 .. 287]])+buildFixedLitTree :: DeflateM s (HuffmanTree Int)+buildFixedLitTree =+  computeHuffmanTree+    ( [(x, 8) | x <- [0 .. 143]]+      ++ [(x, 9) | x <- [144 .. 255]]+        ++ [(x, 7) | x <- [256 .. 279]]+        ++ [(x, 8) | x <- [280 .. 287]]+    ) -buildFixedDistanceTree :: DeflateM (HuffmanTree Int)-buildFixedDistanceTree = computeHuffmanTree [(x,5) | x <- [0..31]]+buildFixedDistanceTree :: DeflateM s (HuffmanTree Int)+buildFixedDistanceTree = computeHuffmanTree [(x, 5) | x <- [0 .. 31]]  -- ----------------------------------------------------------------------------- -computeHuffmanTree :: [(Int, Int)] -> DeflateM (HuffmanTree Int)+computeHuffmanTree :: [(Int, Int)] -> DeflateM s (HuffmanTree Int) computeHuffmanTree initialData =   case createHuffmanTree (computeCodeValues initialData) of-    Left  err -> raise (HuffmanTreeError err)-    Right x   -> return x+    Left err -> raise (HuffmanTreeError err)+    Right x -> return x  computeCodeValues :: [(Int, Int)] -> [(Int, Int, Int)]-computeCodeValues vals = Map.foldrWithKey (\ v (l, c) a -> (v,l,c):a) [] codes+computeCodeValues vals = Map.foldrWithKey (\v (l, c) a -> (v, l, c) : a) [] codes  where-  valsNo0s = filter (\ (_, b) -> (b /= 0)) vals-  valsSort = sortBy (\ (a,_) (b,_) -> compare a b) valsNo0s-  blCount  = foldr (\ (_,k) m -> Map.insertWith (+) k 1 m) Map.empty valsNo0s+  valsNo0s = filter (\(_, b) -> (b /= 0)) vals+  valsSort = sortBy (\(a, _) (b, _) -> compare a b) valsNo0s+  blCount = foldr (\(_, k) m -> Map.insertWith (+) k 1 m) Map.empty valsNo0s   nextcode = step2 0 1 (Map.insert 0 0 Map.empty)-  lenTree  = Map.fromList valsSort+  lenTree = Map.fromList valsSort   codeTree = step3 (map fst valsSort) nextcode Map.empty-  maxBits  = maximum (map snd valsSort)-  codes    = Map.intersectionWith (,) lenTree codeTree+  maxBits = maximum (map snd valsSort)+  codes = Map.intersectionWith (,) lenTree codeTree   --   step2 code bits nc     | bits > maxBits = nc     | otherwise =       let prevCount = Map.findWithDefault 0 (bits - 1) blCount           code' = (code + prevCount) `shiftL` 1-      in step2 code' (bits + 1) (Map.insert bits code' nc) +       in step2 code' (bits + 1) (Map.insert bits code' nc)   --   step3 [] _ ct = ct-  step3 (n:rest) nc ct =-    let len        = Map.findWithDefault 0 n lenTree+  step3 (n : rest) nc ct =+    let len = Map.findWithDefault 0 n lenTree         Just ncLen = Map.lookup len nc-        ct'        = Map.insert n ncLen ct-        nc'        = Map.insert len (ncLen + 1) nc-    in if len == 0-          then step3 rest nc  ct+        ct' = Map.insert n ncLen ct+        nc' = Map.insert len (ncLen + 1) nc+     in if len == 0+          then step3 rest nc ct           else step3 rest nc' ct'  codeLengthOrder :: [Int] codeLengthOrder =   [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]--
− src/Codec/Compression/Zlib/FingerTree.hs
@@ -1,680 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE DeriveGeneric #-}--------------------------------------------------------------------------------- |--- Module      :  Data.FingerTree--- Copyright   :  (c) Ross Paterson, Ralf Hinze 2006--- License     :  BSD-style--- Maintainer  :  R.Paterson@city.ac.uk--- Stability   :  experimental--- Portability :  non-portable (MPTCs and functional dependencies)------ A general sequence representation with arbitrary annotations, for--- use as a base for implementations of various collection types, as--- described in section 4 of------  * Ralf Hinze and Ross Paterson,---    \"Finger trees: a simple general-purpose data structure\",---    /Journal of Functional Programming/ 16:2 (2006) pp 197-217.---    <http://staff.city.ac.uk/~ross/papers/FingerTree.html>------ For a directly usable sequence type, see @Data.Sequence@, which is--- a specialization of this structure.------ An amortized running time is given for each operation, with /n/--- referring to the length of the sequence.  These bounds hold even in--- a persistent (shared) setting.------ /Note/: Many of these operations have the same names as similar--- operations on lists in the "Prelude".  The ambiguity may be resolved--- using either qualification or the @hiding@ clause.-----------------------------------------------------------------------------------module Codec.Compression.Zlib.FingerTree (-    FingerTree,-    Measured(..),-    -- * Construction-    empty,-    (|>),-    -- ** Examining the ends-    dropTakeCombine,-    -- ** Splitting-    -- | These functions are special cases of 'search'.-    split,-    -- * Example-    -- $example-    toBuilder-    ) where--import Prelude hiding (null, reverse)-import GHC.Generics-import qualified Data.ByteString as S-import Data.ByteString.Builder(Builder, byteString)-import qualified Data.ByteString.Lazy as L-import qualified Data.ByteString.Lazy.Internal as L--type Measure = Int--infixr 5 ><-infixr 5 <|, :<-infixl 5 |>, :>---- | View of the left end of a sequence.-data ViewL s a-    = EmptyL        -- ^ empty sequence-    | a :< s a      -- ^ leftmost element and the rest of the sequence-    deriving (Eq, Ord, Show, Read, Generic)---- | View of the right end of a sequence.-data ViewR s a-    = EmptyR        -- ^ empty sequence-    | s a :> a      -- ^ the sequence minus the rightmost element,-                    -- and the rightmost element-    deriving (Eq, Ord, Show, Read, Generic)--instance (Functor s) => Functor (ViewL s) where-    fmap _ EmptyL    = EmptyL-    fmap f (x :< xs) = f x :< fmap f xs--instance (Functor s) => Functor (ViewR s) where-    fmap _ EmptyR    = EmptyR-    fmap f (xs :> x) = fmap f xs :> f x--instance (Measured a) => Semigroup (FingerTree a) where-    (<>) = (><)---- | 'empty' and '><'.-instance (Measured a) => Monoid (FingerTree a) where-    mempty = empty-    mappend = (><)--instance Measured S.ByteString where-  measure = S.length-  {-# INLINE measure #-}---- Explicit Digit type (Exercise 1)--data Digit a-    = One a-    | Two a a-    | Three a a a-    | Four a a a a-    deriving (Show)------------------------ 4.1 Measurements------------------------ | Things that can be measured.-class Measured a where-    measure :: a -> Measure--instance (Measured a) => Measured (Digit a) where-    measure (One a1) = measure a1-    measure (Two a1 a2) = measure a1 + measure a2-    measure (Three a1 a2 a3) = measure a1 + measure a2 + measure a3-    measure (Four a1 a2 a3 a4) = measure a1 + measure a2 + measure a3 + measure a4-------------------------------- 4.2 Caching measurements------------------------------data Node a = Node2 !Measure a a | Node3 !Measure a a a-    deriving (Show)--node2        ::  (Measured a) => a -> a -> Node a-node2 a b    =   Node2 (measure a + measure b) a b--node3        ::  (Measured a) => a -> a -> a -> Node a-node3 a b c  =   Node3 (measure a + measure b + measure c) a b c--instance Measured (Node a) where-    measure (Node2 v _ _)    =  v-    measure (Node3 v _ _ _)  =  v--nodeToDigit :: Node a -> Digit a-nodeToDigit (Node2 _ a b) = Two a b-nodeToDigit (Node3 _ a b c) = Three a b c---- | A representation of a sequence of values of type @a@, allowing--- access to the ends in constant time, and append and split in time--- logarithmic in the size of the smaller piece.------ The collection is also parameterized by a measure type @v@, which--- is used to specify a position in the sequence for the 'split' operation.--- The types of the operations enforce the constraint @'Measured' v a@,--- which also implies that the type @v@ is determined by @a@.------ A variety of abstract data types can be implemented by using different--- element types and measurements.-data FingerTree a-    = Empty-    | Single a-    | Deep !Measure !(Digit a) (FingerTree (Node a)) !(Digit a)-    deriving (Show)--deep ::  (Measured a) => Digit a -> FingerTree (Node a) -> Digit a -> FingerTree a-deep pr m sf =-    Deep ((measure pr + measure m) + measure sf) pr m sf---- | /O(1)/. The cached measure of a tree.-instance (Measured a) => Measured (FingerTree a) where-    measure Empty           =  0-    measure (Single x)      =  measure x-    measure (Deep v _ _ _)  =  v---------------------------------------------------------- 4.3 Construction, deconstruction and concatenation---------------------------------------------------------- | /O(1)/. The empty sequence.-empty :: Measured a => FingerTree a-empty = Empty---- | /O(1)/. Add an element to the left end of a sequence.--- Mnemonic: a triangle with the single element at the pointy end.-(<|) :: (Measured a) => a -> FingerTree a -> FingerTree a-a <| Empty              =  Single a-a <| Single b           =  deep (One a) Empty (One b)-a <| Deep v (Four b c d e) m sf = m `seq`-    Deep (measure a + v) (Two a b) (node3 c d e <| m) sf-a <| Deep v pr m sf     =-    Deep (measure a + v) (consDigit a pr) m sf--consDigit :: a -> Digit a -> Digit a-consDigit a (One b) = Two a b-consDigit a (Two b c) = Three a b c-consDigit a (Three b c d) = Four a b c d-consDigit _ (Four _ _ _ _) = illegal_argument "consDigit"---- | /O(1)/. Add an element to the right end of a sequence.--- Mnemonic: a triangle with the single element at the pointy end.-(|>) :: (Measured a) => FingerTree a -> a -> FingerTree a-Empty |> a              =  Single a-Single a |> b           =  deep (One a) Empty (One b)-Deep v pr m (Four a b c d) |> e = m `seq`-    Deep (v + measure e) pr (m |> node3 a b c) (Two d e)-Deep v pr m sf |> x     =-    Deep (v + measure x) pr m (snocDigit sf x)--snocDigit :: Digit a -> a -> Digit a-snocDigit (One a) b = Two a b-snocDigit (Two a b) c = Three a b c-snocDigit (Three a b c) d = Four a b c d-snocDigit (Four _ _ _ _) _ = illegal_argument "snocDigit"---- | /O(1)/. Analyse the left end of a sequence.-viewl :: (Measured a) => FingerTree a -> ViewL FingerTree a-viewl Empty                     =  EmptyL-viewl (Single x)                =  x :< Empty-viewl (Deep _ (One x) m sf)     =  x :< rotL m sf-viewl (Deep _ pr m sf)          =  lheadDigit pr :< deep (ltailDigit pr) m sf--dropTakeCombine :: Int -> Int -> FingerTree S.ByteString -> L.ByteString-dropTakeCombine !amountToSkip !amountToKeep !tree =-  -- NB: we run this right after calling split, so on we shouldn't ever be in a situation where-  -- the size of the first chunk is less than `amountToSkip`; if that does happy, split pushed-  -- us to the wrong place.-  case tree of-    Empty -> L.empty -    Single x -> go amountToKeep (Single (S.drop amountToSkip x))-    Deep _ (One x) m sf -> go amountToKeep (Deep 0 (One (S.drop amountToSkip x)) m sf)-    Deep _ (Two x r) m sf -> go amountToKeep (Deep 0 (Two (S.drop amountToSkip x) r) m sf)-    Deep _ (Three x r s) m sf -> go amountToKeep (Deep 0 (Three (S.drop amountToSkip x) r s) m sf)-    Deep _ (Four x r s t) m sf -> go amountToKeep (Deep 0 (Four (S.drop amountToSkip x) r s t) m sf)- where-  go 0 _ = L.empty -  go left ftr =-    case ftr of-      Empty -> L.empty -      Single x -> L.Chunk (S.take left x) L.Empty-      Deep _ (One x) m sf -> keepSection left x (rotL m sf)-      Deep _ (Two x r) m sf -> keepSection left x (Deep 0 (One r) m sf)-      Deep _ (Three x r s) m sf -> keepSection left x (Deep 0 (Two r s) m sf)-      Deep _ (Four x r s t) m sf -> keepSection left x (Deep 0 (Three r s t) m sf)--  keepSection left chunk rest-    | S.length chunk >= left = L.Chunk (S.take left chunk) L.Empty -    | otherwise = L.Chunk chunk (go (left - S.length chunk) rest)--rotL :: (Measured a) => FingerTree (Node a) -> Digit a -> FingerTree a-rotL m sf      =   case viewl m of-    EmptyL  ->  digitToTree sf-    a :< m' ->  Deep (measure m + measure sf) (nodeToDigit a) m' sf--lheadDigit :: Digit a -> a-lheadDigit (One a) = a-lheadDigit (Two a _) = a-lheadDigit (Three a _ _) = a-lheadDigit (Four a _ _ _) = a--ltailDigit :: Digit a -> Digit a-ltailDigit (One _) = illegal_argument "ltailDigit"-ltailDigit (Two _ b) = One b-ltailDigit (Three _ b c) = Two b c-ltailDigit (Four _ b c d) = Three b c d---- | /O(1)/. Analyse the right end of a sequence.-viewr :: (Measured a) => FingerTree a -> ViewR FingerTree a-viewr Empty                     =  EmptyR-viewr (Single x)                =  Empty :> x-viewr (Deep _ pr m (One x))     =  rotR pr m :> x-viewr (Deep _ pr m sf)          =  deep pr m (rtailDigit sf) :> rheadDigit sf--rotR :: (Measured a) => Digit a -> FingerTree (Node a) -> FingerTree a-rotR pr m = case viewr m of-    EmptyR  ->  digitToTree pr-    m' :> a ->  Deep (measure pr + measure m) pr m' (nodeToDigit a)--rheadDigit :: Digit a -> a-rheadDigit (One a) = a-rheadDigit (Two _ b) = b-rheadDigit (Three _ _ c) = c-rheadDigit (Four _ _ _ d) = d--rtailDigit :: Digit a -> Digit a-rtailDigit (One _) = illegal_argument "rtailDigit"-rtailDigit (Two a _) = One a-rtailDigit (Three a b _) = Two a b-rtailDigit (Four a b c _) = Three a b c--digitToTree :: (Measured a) => Digit a -> FingerTree a-digitToTree (One a) = Single a-digitToTree (Two a b) = deep (One a) Empty (One b)-digitToTree (Three a b c) = deep (Two a b) Empty (One c)-digitToTree (Four a b c d) = deep (Two a b) Empty (Two c d)--------------------- Concatenation--------------------- | /O(log(min(n1,n2)))/. Concatenate two sequences.-(><) :: (Measured a) => FingerTree a -> FingerTree a -> FingerTree a-(><) =  appendTree0--appendTree0 :: (Measured a) => FingerTree a -> FingerTree a -> FingerTree a-appendTree0 Empty xs =-    xs-appendTree0 xs Empty =-    xs-appendTree0 (Single x) xs =-    x <| xs-appendTree0 xs (Single x) =-    xs |> x-appendTree0 (Deep _ pr1 m1 sf1) (Deep _ pr2 m2 sf2) =-    deep pr1 (addDigits0 m1 sf1 pr2 m2) sf2--addDigits0 :: (Measured a) => FingerTree (Node a) -> Digit a -> Digit a -> FingerTree (Node a) -> FingerTree (Node a)-addDigits0 m1 (One a) (One b) m2 =-    appendTree1 m1 (node2 a b) m2-addDigits0 m1 (One a) (Two b c) m2 =-    appendTree1 m1 (node3 a b c) m2-addDigits0 m1 (One a) (Three b c d) m2 =-    appendTree2 m1 (node2 a b) (node2 c d) m2-addDigits0 m1 (One a) (Four b c d e) m2 =-    appendTree2 m1 (node3 a b c) (node2 d e) m2-addDigits0 m1 (Two a b) (One c) m2 =-    appendTree1 m1 (node3 a b c) m2-addDigits0 m1 (Two a b) (Two c d) m2 =-    appendTree2 m1 (node2 a b) (node2 c d) m2-addDigits0 m1 (Two a b) (Three c d e) m2 =-    appendTree2 m1 (node3 a b c) (node2 d e) m2-addDigits0 m1 (Two a b) (Four c d e f) m2 =-    appendTree2 m1 (node3 a b c) (node3 d e f) m2-addDigits0 m1 (Three a b c) (One d) m2 =-    appendTree2 m1 (node2 a b) (node2 c d) m2-addDigits0 m1 (Three a b c) (Two d e) m2 =-    appendTree2 m1 (node3 a b c) (node2 d e) m2-addDigits0 m1 (Three a b c) (Three d e f) m2 =-    appendTree2 m1 (node3 a b c) (node3 d e f) m2-addDigits0 m1 (Three a b c) (Four d e f g) m2 =-    appendTree3 m1 (node3 a b c) (node2 d e) (node2 f g) m2-addDigits0 m1 (Four a b c d) (One e) m2 =-    appendTree2 m1 (node3 a b c) (node2 d e) m2-addDigits0 m1 (Four a b c d) (Two e f) m2 =-    appendTree2 m1 (node3 a b c) (node3 d e f) m2-addDigits0 m1 (Four a b c d) (Three e f g) m2 =-    appendTree3 m1 (node3 a b c) (node2 d e) (node2 f g) m2-addDigits0 m1 (Four a b c d) (Four e f g h) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node2 g h) m2--appendTree1 :: (Measured a) => FingerTree a -> a -> FingerTree a -> FingerTree a-appendTree1 Empty a xs =-    a <| xs-appendTree1 xs a Empty =-    xs |> a-appendTree1 (Single x) a xs =-    x <| a <| xs-appendTree1 xs a (Single x) =-    xs |> a |> x-appendTree1 (Deep _ pr1 m1 sf1) a (Deep _ pr2 m2 sf2) =-    deep pr1 (addDigits1 m1 sf1 a pr2 m2) sf2--addDigits1 :: (Measured a) => FingerTree (Node a) -> Digit a -> a -> Digit a -> FingerTree (Node a) -> FingerTree (Node a)-addDigits1 m1 (One a) b (One c) m2 =-    appendTree1 m1 (node3 a b c) m2-addDigits1 m1 (One a) b (Two c d) m2 =-    appendTree2 m1 (node2 a b) (node2 c d) m2-addDigits1 m1 (One a) b (Three c d e) m2 =-    appendTree2 m1 (node3 a b c) (node2 d e) m2-addDigits1 m1 (One a) b (Four c d e f) m2 =-    appendTree2 m1 (node3 a b c) (node3 d e f) m2-addDigits1 m1 (Two a b) c (One d) m2 =-    appendTree2 m1 (node2 a b) (node2 c d) m2-addDigits1 m1 (Two a b) c (Two d e) m2 =-    appendTree2 m1 (node3 a b c) (node2 d e) m2-addDigits1 m1 (Two a b) c (Three d e f) m2 =-    appendTree2 m1 (node3 a b c) (node3 d e f) m2-addDigits1 m1 (Two a b) c (Four d e f g) m2 =-    appendTree3 m1 (node3 a b c) (node2 d e) (node2 f g) m2-addDigits1 m1 (Three a b c) d (One e) m2 =-    appendTree2 m1 (node3 a b c) (node2 d e) m2-addDigits1 m1 (Three a b c) d (Two e f) m2 =-    appendTree2 m1 (node3 a b c) (node3 d e f) m2-addDigits1 m1 (Three a b c) d (Three e f g) m2 =-    appendTree3 m1 (node3 a b c) (node2 d e) (node2 f g) m2-addDigits1 m1 (Three a b c) d (Four e f g h) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node2 g h) m2-addDigits1 m1 (Four a b c d) e (One f) m2 =-    appendTree2 m1 (node3 a b c) (node3 d e f) m2-addDigits1 m1 (Four a b c d) e (Two f g) m2 =-    appendTree3 m1 (node3 a b c) (node2 d e) (node2 f g) m2-addDigits1 m1 (Four a b c d) e (Three f g h) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node2 g h) m2-addDigits1 m1 (Four a b c d) e (Four f g h i) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node3 g h i) m2--appendTree2 :: (Measured a) => FingerTree a -> a -> a -> FingerTree a -> FingerTree a-appendTree2 Empty a b xs =-    a <| b <| xs-appendTree2 xs a b Empty =-    xs |> a |> b-appendTree2 (Single x) a b xs =-    x <| a <| b <| xs-appendTree2 xs a b (Single x) =-    xs |> a |> b |> x-appendTree2 (Deep _ pr1 m1 sf1) a b (Deep _ pr2 m2 sf2) =-    deep pr1 (addDigits2 m1 sf1 a b pr2 m2) sf2--addDigits2 :: (Measured a) => FingerTree (Node a) -> Digit a -> a -> a -> Digit a -> FingerTree (Node a) -> FingerTree (Node a)-addDigits2 m1 (One a) b c (One d) m2 =-    appendTree2 m1 (node2 a b) (node2 c d) m2-addDigits2 m1 (One a) b c (Two d e) m2 =-    appendTree2 m1 (node3 a b c) (node2 d e) m2-addDigits2 m1 (One a) b c (Three d e f) m2 =-    appendTree2 m1 (node3 a b c) (node3 d e f) m2-addDigits2 m1 (One a) b c (Four d e f g) m2 =-    appendTree3 m1 (node3 a b c) (node2 d e) (node2 f g) m2-addDigits2 m1 (Two a b) c d (One e) m2 =-    appendTree2 m1 (node3 a b c) (node2 d e) m2-addDigits2 m1 (Two a b) c d (Two e f) m2 =-    appendTree2 m1 (node3 a b c) (node3 d e f) m2-addDigits2 m1 (Two a b) c d (Three e f g) m2 =-    appendTree3 m1 (node3 a b c) (node2 d e) (node2 f g) m2-addDigits2 m1 (Two a b) c d (Four e f g h) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node2 g h) m2-addDigits2 m1 (Three a b c) d e (One f) m2 =-    appendTree2 m1 (node3 a b c) (node3 d e f) m2-addDigits2 m1 (Three a b c) d e (Two f g) m2 =-    appendTree3 m1 (node3 a b c) (node2 d e) (node2 f g) m2-addDigits2 m1 (Three a b c) d e (Three f g h) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node2 g h) m2-addDigits2 m1 (Three a b c) d e (Four f g h i) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node3 g h i) m2-addDigits2 m1 (Four a b c d) e f (One g) m2 =-    appendTree3 m1 (node3 a b c) (node2 d e) (node2 f g) m2-addDigits2 m1 (Four a b c d) e f (Two g h) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node2 g h) m2-addDigits2 m1 (Four a b c d) e f (Three g h i) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node3 g h i) m2-addDigits2 m1 (Four a b c d) e f (Four g h i j) m2 =-    appendTree4 m1 (node3 a b c) (node3 d e f) (node2 g h) (node2 i j) m2--appendTree3 :: (Measured a) => FingerTree a -> a -> a -> a -> FingerTree a -> FingerTree a-appendTree3 Empty a b c xs =-    a <| b <| c <| xs-appendTree3 xs a b c Empty =-    xs |> a |> b |> c-appendTree3 (Single x) a b c xs =-    x <| a <| b <| c <| xs-appendTree3 xs a b c (Single x) =-    xs |> a |> b |> c |> x-appendTree3 (Deep _ pr1 m1 sf1) a b c (Deep _ pr2 m2 sf2) =-    deep pr1 (addDigits3 m1 sf1 a b c pr2 m2) sf2--addDigits3 :: (Measured a) => FingerTree (Node a) -> Digit a -> a -> a -> a -> Digit a -> FingerTree (Node a) -> FingerTree (Node a)-addDigits3 m1 (One a) b c d (One e) m2 =-    appendTree2 m1 (node3 a b c) (node2 d e) m2-addDigits3 m1 (One a) b c d (Two e f) m2 =-    appendTree2 m1 (node3 a b c) (node3 d e f) m2-addDigits3 m1 (One a) b c d (Three e f g) m2 =-    appendTree3 m1 (node3 a b c) (node2 d e) (node2 f g) m2-addDigits3 m1 (One a) b c d (Four e f g h) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node2 g h) m2-addDigits3 m1 (Two a b) c d e (One f) m2 =-    appendTree2 m1 (node3 a b c) (node3 d e f) m2-addDigits3 m1 (Two a b) c d e (Two f g) m2 =-    appendTree3 m1 (node3 a b c) (node2 d e) (node2 f g) m2-addDigits3 m1 (Two a b) c d e (Three f g h) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node2 g h) m2-addDigits3 m1 (Two a b) c d e (Four f g h i) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node3 g h i) m2-addDigits3 m1 (Three a b c) d e f (One g) m2 =-    appendTree3 m1 (node3 a b c) (node2 d e) (node2 f g) m2-addDigits3 m1 (Three a b c) d e f (Two g h) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node2 g h) m2-addDigits3 m1 (Three a b c) d e f (Three g h i) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node3 g h i) m2-addDigits3 m1 (Three a b c) d e f (Four g h i j) m2 =-    appendTree4 m1 (node3 a b c) (node3 d e f) (node2 g h) (node2 i j) m2-addDigits3 m1 (Four a b c d) e f g (One h) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node2 g h) m2-addDigits3 m1 (Four a b c d) e f g (Two h i) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node3 g h i) m2-addDigits3 m1 (Four a b c d) e f g (Three h i j) m2 =-    appendTree4 m1 (node3 a b c) (node3 d e f) (node2 g h) (node2 i j) m2-addDigits3 m1 (Four a b c d) e f g (Four h i j k) m2 =-    appendTree4 m1 (node3 a b c) (node3 d e f) (node3 g h i) (node2 j k) m2--appendTree4 :: (Measured a) => FingerTree a -> a -> a -> a -> a -> FingerTree a -> FingerTree a-appendTree4 Empty a b c d xs =-    a <| b <| c <| d <| xs-appendTree4 xs a b c d Empty =-    xs |> a |> b |> c |> d-appendTree4 (Single x) a b c d xs =-    x <| a <| b <| c <| d <| xs-appendTree4 xs a b c d (Single x) =-    xs |> a |> b |> c |> d |> x-appendTree4 (Deep _ pr1 m1 sf1) a b c d (Deep _ pr2 m2 sf2) =-    deep pr1 (addDigits4 m1 sf1 a b c d pr2 m2) sf2--addDigits4 :: (Measured a) => FingerTree (Node a) -> Digit a -> a -> a -> a -> a -> Digit a -> FingerTree (Node a) -> FingerTree (Node a)-addDigits4 m1 (One a) b c d e (One f) m2 =-    appendTree2 m1 (node3 a b c) (node3 d e f) m2-addDigits4 m1 (One a) b c d e (Two f g) m2 =-    appendTree3 m1 (node3 a b c) (node2 d e) (node2 f g) m2-addDigits4 m1 (One a) b c d e (Three f g h) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node2 g h) m2-addDigits4 m1 (One a) b c d e (Four f g h i) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node3 g h i) m2-addDigits4 m1 (Two a b) c d e f (One g) m2 =-    appendTree3 m1 (node3 a b c) (node2 d e) (node2 f g) m2-addDigits4 m1 (Two a b) c d e f (Two g h) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node2 g h) m2-addDigits4 m1 (Two a b) c d e f (Three g h i) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node3 g h i) m2-addDigits4 m1 (Two a b) c d e f (Four g h i j) m2 =-    appendTree4 m1 (node3 a b c) (node3 d e f) (node2 g h) (node2 i j) m2-addDigits4 m1 (Three a b c) d e f g (One h) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node2 g h) m2-addDigits4 m1 (Three a b c) d e f g (Two h i) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node3 g h i) m2-addDigits4 m1 (Three a b c) d e f g (Three h i j) m2 =-    appendTree4 m1 (node3 a b c) (node3 d e f) (node2 g h) (node2 i j) m2-addDigits4 m1 (Three a b c) d e f g (Four h i j k) m2 =-    appendTree4 m1 (node3 a b c) (node3 d e f) (node3 g h i) (node2 j k) m2-addDigits4 m1 (Four a b c d) e f g h (One i) m2 =-    appendTree3 m1 (node3 a b c) (node3 d e f) (node3 g h i) m2-addDigits4 m1 (Four a b c d) e f g h (Two i j) m2 =-    appendTree4 m1 (node3 a b c) (node3 d e f) (node2 g h) (node2 i j) m2-addDigits4 m1 (Four a b c d) e f g h (Three i j k) m2 =-    appendTree4 m1 (node3 a b c) (node3 d e f) (node3 g h i) (node2 j k) m2-addDigits4 m1 (Four a b c d) e f g h (Four i j k l) m2 =-    appendTree4 m1 (node3 a b c) (node3 d e f) (node3 g h i) (node3 j k l) m2--------------------- 4.4 Splitting--------------------- | /O(log(min(i,n-i)))/. Split a sequence at a point where the predicate--- on the accumulated measure of the prefix changes from 'False' to 'True'.------ For predictable results, one should ensure that there is only one such--- point, i.e. that the predicate is /monotonic/.-split ::  (Measured a) =>-      Measure -> FingerTree a -> (FingerTree a, FingerTree a)-split _ Empty  =  (Empty, Empty)-split p xs-  | (measure xs) >= p =  (l, x <| r)-  | otherwise   =  (xs, Empty)-  where-    Split l x r = splitTree p 0 xs--data Split t a = Split t a t--splitTree :: (Measured a) =>-    Measure -> Measure -> FingerTree a -> Split (FingerTree a) a-splitTree _ _ Empty = illegal_argument "splitTree"-splitTree _ _ (Single x) = Split Empty x Empty-splitTree p i (Deep _ pr m sf)-  | vpr > p     =  let  Split l x r     =  splitDigit p i pr-                   in   Split (maybe Empty digitToTree l) x (deepL r m sf)-  | vm > p      =  let  Split ml xs mr  =  splitTree p vpr m-                        Split l x r     =  splitNode p (vpr + measure ml) xs-                   in   Split (deepR pr  ml l) x (deepL r mr sf)-  | otherwise   =  let  Split l x r     =  splitDigit p vm sf-                   in   Split (deepR pr  m  l) x (maybe Empty digitToTree r)-  where-    vpr     =  i    +  measure pr-    vm      =  vpr  +  measure m--deepL :: (Measured a) =>-    Maybe (Digit a) -> FingerTree (Node a) -> Digit a -> FingerTree a-deepL Nothing m sf      =   rotL m sf-deepL (Just pr) m sf    =   deep pr m sf--deepR :: (Measured a) =>-    Digit a -> FingerTree (Node a) -> Maybe (Digit a) -> FingerTree a-deepR pr m Nothing      =   rotR pr m-deepR pr m (Just sf)    =   deep pr m sf--splitNode :: (Measured a) =>-    Measure -> Measure -> Node a -> Split (Maybe (Digit a)) a-splitNode p i (Node2 _ a b)-  | va > p      = Split Nothing a (Just (One b))-  | otherwise   = Split (Just (One a)) b Nothing-  where-    va      = i + measure a-splitNode p i (Node3 _ a b c)-  | va > p      = Split Nothing a (Just (Two b c))-  | vab > p     = Split (Just (One a)) b (Just (One c))-  | otherwise   = Split (Just (Two a b)) c Nothing-  where-    va      = i + measure a-    vab     = va + measure b--splitDigit :: (Measured a) =>-    Measure  -> Measure -> Digit a -> Split (Maybe (Digit a)) a-splitDigit _ i (One a) = i `seq` Split Nothing a Nothing-splitDigit p i (Two a b)-  | va > p      = Split Nothing a (Just (One b))-  | otherwise   = Split (Just (One a)) b Nothing-  where-    va      = i + measure a-splitDigit p i (Three a b c)-  | va > p      = Split Nothing a (Just (Two b c))-  | vab > p     = Split (Just (One a)) b (Just (One c))-  | otherwise   = Split (Just (Two a b)) c Nothing-  where-    va      = i + measure a-    vab     = va + measure b-splitDigit p i (Four a b c d)-  | va > p      = Split Nothing a (Just (Three b c d))-  | vab > p     = Split (Just (One a)) b (Just (Two c d))-  | vabc > p    = Split (Just (Two a b)) c (Just (One d))-  | otherwise   = Split (Just (Three a b c)) d Nothing-  where-    va      = i + measure a-    vab     = va + measure b-    vabc    = vab + measure c----------------------- Transformations---------------------illegal_argument :: String -> a-illegal_argument name =-    error $ "Logic error: " ++ name ++ " called with illegal argument"--{- $example--Particular abstract data types may be implemented by defining-element types with suitable 'Measured' instances.--(from section 4.5 of the paper)-Simple sequences can be implemented using a 'Sum' monoid as a measure:--> newtype Elem a = Elem { getElem :: a }->-> instance Measured (Sum Int) (Elem a) where->     measure (Elem _) = Sum 1->-> newtype Seq a = Seq (FingerTree (Sum Int) (Elem a))--Then the measure of a subsequence is simply its length.-This representation supports log-time extraction of subsequences:--> take :: Int -> Seq a -> Seq a-> take k (Seq xs) = Seq (takeUntil (> Sum k) xs)->-> drop :: Int -> Seq a -> Seq a-> drop k (Seq xs) = Seq (dropUntil (> Sum k) xs)--The module @Data.Sequence@ is an optimized instantiation of this type.--For further examples, see "Data.IntervalMap.FingerTree" and-"Data.PriorityQueue.FingerTree".---}--class ToBuilder a where-  toBuilder :: a -> Builder--instance ToBuilder S.ByteString where-  toBuilder x = byteString x--instance ToBuilder a => ToBuilder (FingerTree a) where-  toBuilder ft =-    case ft of-      Empty -> mempty-      Single x -> toBuilder x-      Deep _ a b c -> toBuilder a <> toBuilder b <> toBuilder c--instance ToBuilder a => ToBuilder (Node a) where-  toBuilder n =-    case n of-      Node2 _ a b -> toBuilder a <> toBuilder b-      Node3 _ a b c -> toBuilder a <> toBuilder b <> toBuilder c--instance ToBuilder a => ToBuilder (Digit a) where-  toBuilder d =-    case d of-      One a -> toBuilder a-      Two a b -> toBuilder a <> toBuilder b-      Three a b c -> toBuilder a <> toBuilder b <> toBuilder c-      Four a b c e -> toBuilder a <> toBuilder b <> toBuilder c <> toBuilder e- 
src/Codec/Compression/Zlib/HuffmanTree.hs view
@@ -1,56 +1,66 @@-module Codec.Compression.Zlib.HuffmanTree(-         HuffmanTree-       , AdvanceResult(..)-       , createHuffmanTree-       , advanceTree-       )- where+module Codec.Compression.Zlib.HuffmanTree (+  HuffmanTree,+  AdvanceResult (..),+  createHuffmanTree,+  advanceTree,+) where -import Data.Bits(testBit)-import Data.Word(Word8)+import Data.Bits (testBit)+import Data.Word (Word8) -data HuffmanTree a = HuffmanNode (HuffmanTree a) (HuffmanTree a)-                   | HuffmanValue a-                   | HuffmanEmpty- deriving (Show)+data HuffmanTree a+  = HuffmanNode (HuffmanTree a) (HuffmanTree a)+  | HuffmanValue a+  | HuffmanEmpty+  deriving (Show) -data AdvanceResult a = AdvanceError String-                     | NewTree (HuffmanTree a)-                     | Result a+data AdvanceResult a+  = AdvanceError String+  | NewTree (HuffmanTree a)+  | Result a  emptyHuffmanTree :: HuffmanTree a emptyHuffmanTree = HuffmanEmpty -createHuffmanTree :: Show a =>-                     [(a, Int, Int)] ->-                     Either String (HuffmanTree a)+createHuffmanTree ::+  Show a =>+  [(a, Int, Int)] ->+  Either String (HuffmanTree a) createHuffmanTree = foldr addHuffmanNode' (Right emptyHuffmanTree)- where addHuffmanNode' (a, b, c) acc =-         case acc of-           Left err   -> Left err-           Right tree -> addHuffmanNode a b c tree+ where+  addHuffmanNode' (a, b, c) acc =+    case acc of+      Left err -> Left err+      Right tree -> addHuffmanNode a b c tree -addHuffmanNode :: Show a =>-                  a -> Int -> Int -> HuffmanTree a ->-                  Either String (HuffmanTree a)+addHuffmanNode ::+  Show a =>+  a ->+  Int ->+  Int ->+  HuffmanTree a ->+  Either String (HuffmanTree a) addHuffmanNode val len code node =   case node of-    HuffmanEmpty    | len == 0 ->-      Right (HuffmanValue val)+    HuffmanEmpty+      | len == 0 ->+        Right (HuffmanValue val)     HuffmanEmpty ->       case addHuffmanNode val (len - 1) code HuffmanEmpty of         Left err -> Left err         Right newNode           | testBit code (len - 1) -> Right (HuffmanNode HuffmanEmpty newNode)-          | otherwise              -> Right (HuffmanNode newNode      HuffmanEmpty)+          | otherwise -> Right (HuffmanNode newNode HuffmanEmpty)     ---    HuffmanValue _  | len == 0 ->-      Left "Two values point to the same place!"+    HuffmanValue _+      | len == 0 ->+        Left "Two values point to the same place!"     HuffmanValue _ ->       Left "HuffmanValue hit while inserting a value!"     ---    HuffmanNode _ _ | len == 0 ->-      Left ("Tried to add where the leaf is a node: " ++ show val)+    HuffmanNode _ _+      | len == 0 ->+        Left ("Tried to add where the leaf is a node: " ++ show val)     HuffmanNode l r | testBit code (len - 1) ->       case addHuffmanNode val (len - 1) code r of         Left err -> Left err@@ -63,11 +73,11 @@ advanceTree :: Word8 -> HuffmanTree a -> AdvanceResult a advanceTree x node =   case node of-    HuffmanEmpty     -> AdvanceError "Tried to advance empty tree!"-    HuffmanValue _   -> AdvanceError "Tried to advance value!"-    HuffmanNode  l r ->+    HuffmanEmpty -> AdvanceError "Tried to advance empty tree!"+    HuffmanValue _ -> AdvanceError "Tried to advance value!"+    HuffmanNode l r ->       case if (x == 1) then r else l of-        HuffmanEmpty   -> AdvanceError "Advanced to empty tree!"+        HuffmanEmpty -> AdvanceError "Advanced to empty tree!"         HuffmanValue y -> Result y-        t              -> NewTree t+        t -> NewTree t {-# INLINE advanceTree #-}
src/Codec/Compression/Zlib/Monad.hs view
@@ -1,307 +1,358 @@-{-# LANGUAGE DeriveDataTypeable         #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiWayIf                 #-}-{-# LANGUAGE Rank2Types                 #-}-module Codec.Compression.Zlib.Monad(-         DeflateM-       , runDeflateM-       , ZlibDecoder(..)-       , raise-       , DecompressionError(..)-         -- * Getting data from the input stream.-       , nextBits-       , nextByte-       , nextWord16-       , nextWord32-       , nextBlock-       , nextCode-         -- * Aligning-       , advanceToByte-         -- * Emitting data into the output window-       , emitByte-       , emitBlock-       , emitPastChunk-         -- * Getting and publishing output-       , finalAdler-       , moveWindow-       , finalize-       )- where+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE Rank2Types #-} -import           Codec.Compression.Zlib.Adler32(AdlerState, initialAdlerState,-                                                advanceAdler, advanceAdlerBlock,-                                                finalizeAdler)-import           Codec.Compression.Zlib.HuffmanTree(HuffmanTree, advanceTree,-                                                    AdvanceResult(..))-import           Codec.Compression.Zlib.OutputWindow(OutputWindow, emptyWindow,-                                                     emitExcess, addByte,-                                                     addChunk, addOldChunk,-                                                     finalizeWindow)-import           Control.Exception(Exception)-import           Control.Monad(Monad)-import           Data.Bits(Bits(..))-import qualified Data.ByteString      as S+module Codec.Compression.Zlib.Monad (+  DeflateM,+  runDeflateM,+  ZlibDecoder (..),+  raise,+  DecompressionError (..),++  -- * Getting data from the input stream.+  nextBits,+  nextByte,+  nextWord16,+  nextWord32,+  nextBlock,+  nextCode,++  -- * Aligning+  advanceToByte,++  -- * Emitting data into the output window+  emitByte,+  emitBlock,+  emitPastChunk,++  -- * Getting and publishing output+  finalAdler,+  moveWindow,+  finalize,+) where++import Codec.Compression.Zlib.Adler32 (+  AdlerState,+  advanceAdler,+  advanceAdlerBlock,+  finalizeAdler,+  initialAdlerState,+ )+import Codec.Compression.Zlib.HuffmanTree (+  AdvanceResult (..),+  HuffmanTree,+  advanceTree,+ )+import Codec.Compression.Zlib.OutputWindow (+  OutputWindow,+  addByte,+  addChunk,+  addOldChunk,+  emitExcess,+  emptyWindow,+  finalizeWindow,+ )+import Control.Exception (Exception)+import Data.Bits (Bits (..))+import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L-import           Data.Int(Int64)-import           Data.Typeable(Typeable)-import           Data.Word(Word32, Word16, Word8)-import           Prelude()-import           Prelude.Compat+import Data.Int (Int64)+import Data.Typeable (Typeable)+import Data.Word (Word16, Word32, Word8)+import GHC.ST (ST)+import Prelude.Compat+import Prelude () -data DecompressionState = DecompressionState {-       dcsNextBitNo     :: !Int-     , dcsCurByte       :: !Word8-     , dcsAdler32       :: !AdlerState-     , dcsInput         :: !S.ByteString-     , dcsOutput        :: !OutputWindow-     }+data DecompressionState s = DecompressionState+  { dcsNextBitNo :: !Int+  , dcsCurByte :: !Word8+  , dcsAdler32 :: !AdlerState+  , dcsInput :: !S.ByteString+  , dcsOutput :: !(OutputWindow s)+  } -instance Show DecompressionState where-  show dcs = "DecompressionState<nextBit=" ++ show (dcsNextBitNo dcs) ++ "," ++-             "curByte=" ++ show (dcsCurByte dcs) ++ ",inputLen=" ++-             show (S.length (dcsInput dcs)) ++ ">"+instance Show (DecompressionState s) where+  show dcs =+    "DecompressionState<nextBit=" ++ show (dcsNextBitNo dcs) ++ ","+      ++ "curByte="+      ++ show (dcsCurByte dcs)+      ++ ",inputLen="+      ++ show (S.length (dcsInput dcs))+      ++ ">"  -- ----------------------------------------------------------------------------- -data DecompressionError = HuffmanTreeError   String-                        | FormatError        String-                        | DecompressionError String-                        | HeaderError        String-                        | ChecksumError      String+data DecompressionError+  = HuffmanTreeError String+  | FormatError String+  | DecompressionError String+  | HeaderError String+  | ChecksumError String   deriving (Typeable, Eq)  instance Show DecompressionError where   show x =     case x of-      HuffmanTreeError   s -> "Huffman tree manipulation error: " ++ s-      FormatError        s -> "Block format error: " ++ s+      HuffmanTreeError s -> "Huffman tree manipulation error: " ++ s+      FormatError s -> "Block format error: " ++ s       DecompressionError s -> "Decompression error: " ++ s-      HeaderError        s -> "Header error: " ++ s-      ChecksumError      s -> "Checksum error: " ++ s+      HeaderError s -> "Header error: " ++ s+      ChecksumError s -> "Checksum error: " ++ s  instance Exception DecompressionError  -- ----------------------------------------------------------------------------- -newtype DeflateM a = DeflateM {-    unDeflateM :: DecompressionState ->-                  (DecompressionState -> a -> ZlibDecoder) ->-                  ZlibDecoder+newtype DeflateM s a = DeflateM+  { unDeflateM ::+      DecompressionState s ->+      (DecompressionState s -> a -> ST s (ZlibDecoder s)) ->+      ST s (ZlibDecoder s)   } -instance Applicative DeflateM where-  pure  x = DeflateM (\ s k -> k s x)+instance Applicative (DeflateM s) where+  pure x = DeflateM (\s k -> k s x) -  f <*> x = DeflateM $ \ s1 k ->-     unDeflateM f s1 $ \ s2 g ->-     unDeflateM x s2 $ \ s3 y -> k s3 (g y)+  f <*> x = DeflateM $ \s1 k ->+    unDeflateM f s1 $ \s2 g ->+      unDeflateM x s2 $ \s3 y -> k s3 (g y) -  m *> n = DeflateM $ \ s1 k ->-    unDeflateM m s1 $ \ s2 _ -> unDeflateM n s2 k+  m *> n = DeflateM $ \s1 k ->+    unDeflateM m s1 $ \s2 _ -> unDeflateM n s2 k    {-# INLINE pure #-}   {-# INLINE (<*>) #-}   {-# INLINE (*>) #-} --instance Functor DeflateM where+instance Functor (DeflateM s) where   fmap f m = DeflateM (\s k -> unDeflateM m s (\s' a -> k s' (f a)))   {-# INLINE fmap #-} -instance Monad DeflateM where+instance Monad (DeflateM s) where   {-# INLINE return #-}   return = pure    {-# INLINE (>>=) #-}-  m >>= f = DeflateM $ \ s1 k ->-     unDeflateM m s1 $ \ s2 a -> unDeflateM (f a) s2 k+  m >>= f = DeflateM $ \s1 k ->+    unDeflateM m s1 $ \s2 a -> unDeflateM (f a) s2 k    (>>) = (*>)   {-# INLINE (>>) #-} -get :: DeflateM DecompressionState-get = DeflateM (\ s k -> k s s)+get :: DeflateM s (DecompressionState s)+get = DeflateM (\s k -> k s s) {-# INLINE get #-} -set :: DecompressionState -> DeflateM ()-set !s = DeflateM (\ _ k -> k s ())+set :: DecompressionState s -> DeflateM s ()+set !s = DeflateM (\_ k -> k s ()) {-# INLINE set #-} -raise :: DecompressionError -> DeflateM a-raise e = DeflateM (\ _ _ -> DecompError e)+raise :: DecompressionError -> DeflateM s a+raise e = DeflateM (\_ _ -> return (DecompError e)) {-# INLINE raise #-} -initialState :: DecompressionState-initialState = DecompressionState {-    dcsNextBitNo = 8-  , dcsCurByte   = 0-  , dcsAdler32   = initialAdlerState-  , dcsInput     = S.empty-  , dcsOutput    = emptyWindow-  }+liftST :: ST s a -> DeflateM s a+liftST action = DeflateM $ \s k -> do+  res <- action+  k s res  -- ----------------------------------------------------------------------------- -data ZlibDecoder = NeedMore (S.ByteString -> ZlibDecoder)-                 | Chunk L.ByteString ZlibDecoder-                 | Done-                 | DecompError DecompressionError+data ZlibDecoder s+  = NeedMore (S.ByteString -> ST s (ZlibDecoder s))+  | Chunk S.ByteString (ST s (ZlibDecoder s))+  | Done+  | DecompError DecompressionError -runDeflateM :: DeflateM () -> ZlibDecoder-runDeflateM m = unDeflateM m initialState (\ _ _ -> Done)+runDeflateM :: DeflateM s () -> ST s (ZlibDecoder s)+runDeflateM m = do+  window <- emptyWindow+  let initialState =+        DecompressionState+          { dcsNextBitNo = 8+          , dcsCurByte = 0+          , dcsAdler32 = initialAdlerState+          , dcsInput = S.empty+          , dcsOutput = window+          }+  unDeflateM m initialState (\_ _ -> return Done) {-# INLINE runDeflateM #-}  -- ----------------------------------------------------------------------------- -getNextChunk :: DeflateM ()-getNextChunk = DeflateM $ \ st k -> NeedMore (loadChunk st k)+getNextChunk :: DeflateM s ()+getNextChunk = DeflateM $ \st k -> return (NeedMore (loadChunk st k))  where+  loadChunk ::+    DecompressionState s ->+    (DecompressionState s -> () -> ST s (ZlibDecoder s)) ->+    S.ByteString ->+    ST s (ZlibDecoder s)   loadChunk st k bstr =     case S.uncons bstr of-      Nothing -> NeedMore (loadChunk st k)+      Nothing -> return (NeedMore (loadChunk st k))       Just (nextb, rest) ->-         k st { dcsNextBitNo = 0, dcsCurByte = nextb, dcsInput = rest } ()+        k st{dcsNextBitNo = 0, dcsCurByte = nextb, dcsInput = rest} () -{-# SPECIALIZE nextBits :: Int -> DeflateM Word8 #-}-{-# SPECIALIZE nextBits :: Int -> DeflateM Int   #-}-{-# SPECIALIZE nextBits :: Int -> DeflateM Int64 #-}+{-# SPECIALIZE nextBits :: Int -> DeflateM s Word8 #-}+{-# SPECIALIZE nextBits :: Int -> DeflateM s Int #-}+{-# SPECIALIZE nextBits :: Int -> DeflateM s Int64 #-} {-# INLINE nextBits #-}-nextBits :: (Num a, Bits a) => Int -> DeflateM a+nextBits :: (Num a, Bits a) => Int -> DeflateM s a nextBits x = nextBits' x 0 0 -{-# SPECIALIZE nextBits' :: Int -> Int -> Word8 -> DeflateM Word8 #-}-{-# SPECIALIZE nextBits' :: Int -> Int -> Int   -> DeflateM Int   #-}-{-# SPECIALIZE nextBits' :: Int -> Int -> Int64 -> DeflateM Int64 #-}+{-# SPECIALIZE nextBits' :: Int -> Int -> Word8 -> DeflateM s Word8 #-}+{-# SPECIALIZE nextBits' :: Int -> Int -> Int -> DeflateM s Int #-}+{-# SPECIALIZE nextBits' :: Int -> Int -> Int64 -> DeflateM s Int64 #-} {-# INLINE nextBits' #-}-nextBits' :: (Num a, Bits a) => Int -> Int -> a -> DeflateM a+nextBits' :: (Num a, Bits a) => Int -> Int -> a -> DeflateM s a nextBits' !x' !shiftNum !acc-  | x' == 0       = return acc-  | otherwise     =-      do dcs <- get-         case dcsNextBitNo dcs of-           8 -> case S.uncons (dcsInput dcs) of-                  Nothing ->-                    do getNextChunk -                       nextBits' x' shiftNum acc-                  Just (nextb, rest) ->-                    do set dcs{dcsNextBitNo=0,dcsCurByte=nextb,dcsInput=rest}-                       nextBits' x' shiftNum acc-           nextBitNo ->-             do let !myBits = min x' (8 - nextBitNo)-                    !base   = dcsCurByte dcs `shiftR` nextBitNo-                    !mask   = complement (0xFF `shiftL` myBits)-                    !res    = fromIntegral (base .&. mask)-                    !acc'   = acc .|. (res `shiftL` shiftNum)-                set dcs { dcsNextBitNo=nextBitNo + myBits }-                nextBits' (x' - myBits) (shiftNum + myBits) acc'+  | x' == 0 = return acc+  | otherwise = do+    dcs <- get+    case dcsNextBitNo dcs of+      8 -> case S.uncons (dcsInput dcs) of+        Nothing -> do+          getNextChunk+          nextBits' x' shiftNum acc+        Just (nextb, rest) -> do+          set dcs{dcsNextBitNo = 0, dcsCurByte = nextb, dcsInput = rest}+          nextBits' x' shiftNum acc+      nextBitNo -> do+        let !myBits = min x' (8 - nextBitNo)+            !base = dcsCurByte dcs `shiftR` nextBitNo+            !mask = complement (0xFF `shiftL` myBits)+            !res = fromIntegral (base .&. mask)+            !acc' = acc .|. (res `shiftL` shiftNum)+        set dcs{dcsNextBitNo = nextBitNo + myBits}+        nextBits' (x' - myBits) (shiftNum + myBits) acc' -nextByte :: DeflateM Word8-nextByte =-  do dcs <- get-     if | dcsNextBitNo dcs == 0 -> do set dcs{ dcsNextBitNo = 8 }-                                      return (dcsCurByte dcs)-        | dcsNextBitNo dcs /= 8 -> nextBits 8 -- we're not aligned. sigh.-        | otherwise             -> case S.uncons (dcsInput dcs) of-                                     Nothing -> getNextChunk >> nextByte-                                     Just (nextb, rest) ->-                                       do set dcs{ dcsNextBitNo = 8,-                                                   dcsCurByte   = nextb,-                                                   dcsInput     = rest }-                                          return nextb+nextByte :: DeflateM s Word8+nextByte = do+  dcs <- get+  if+      | dcsNextBitNo dcs == 0 -> do+        set dcs{dcsNextBitNo = 8}+        return (dcsCurByte dcs)+      | dcsNextBitNo dcs /= 8 -> nextBits 8 -- we're not aligned. sigh.+      | otherwise -> case S.uncons (dcsInput dcs) of+        Nothing -> getNextChunk >> nextByte+        Just (nextb, rest) -> do+          set+            dcs+              { dcsNextBitNo = 8+              , dcsCurByte = nextb+              , dcsInput = rest+              }+          return nextb -nextWord16 :: DeflateM Word16-nextWord16 =-  do low  <- fromIntegral `fmap` nextByte-     high <- fromIntegral `fmap` nextByte-     return ((high `shiftL` 8) .|. low)+nextWord16 :: DeflateM s Word16+nextWord16 = do+  low <- fromIntegral `fmap` nextByte+  high <- fromIntegral `fmap` nextByte+  return ((high `shiftL` 8) .|. low) -nextWord32 :: DeflateM Word32-nextWord32 =-  do a <- fromIntegral `fmap` nextByte-     b <- fromIntegral `fmap` nextByte-     c <- fromIntegral `fmap` nextByte-     d <- fromIntegral `fmap` nextByte-     return ((a `shiftL` 24) .|. (b `shiftL` 16) .|. (c `shiftL` 8) .|. d)+nextWord32 :: DeflateM s Word32+nextWord32 = do+  a <- fromIntegral `fmap` nextByte+  b <- fromIntegral `fmap` nextByte+  c <- fromIntegral `fmap` nextByte+  d <- fromIntegral `fmap` nextByte+  return ((a `shiftL` 24) .|. (b `shiftL` 16) .|. (c `shiftL` 8) .|. d) -nextBlock :: Integral a => a -> DeflateM L.ByteString-nextBlock amt =-  do dcs <- get-     if | dcsNextBitNo dcs == 0 ->-            do let startByte = dcsCurByte dcs-               set dcs{ dcsNextBitNo = 8 }-               rest <- nextBlock (amt - 1)-               return (L.cons startByte rest)-        | dcsNextBitNo dcs == 8 ->-            getBlock (fromIntegral amt) (dcsInput dcs)-        | otherwise             ->-            raise (FormatError "Can't get a block on a non-byte boundary.")+nextBlock :: Integral a => a -> DeflateM s L.ByteString+nextBlock amt = do+  dcs <- get+  if+      | dcsNextBitNo dcs == 0 -> do+        let startByte = dcsCurByte dcs+        set dcs{dcsNextBitNo = 8}+        rest <- nextBlock (amt - 1)+        return (L.cons startByte rest)+      | dcsNextBitNo dcs == 8 ->+        getBlock (fromIntegral amt) (dcsInput dcs)+      | otherwise ->+        raise (FormatError "Can't get a block on a non-byte boundary.")  where   getBlock len bstr-    | len < S.length bstr = do let (mine, rest) = S.splitAt len bstr-                               dcs <- get-                               set dcs{ dcsNextBitNo = 8, dcsInput = rest }-                               return (L.fromStrict mine)-    | S.null bstr         = do getNextChunk-                               dcs <- get-                               let byte1 = dcsCurByte dcs-                               rest <- getBlock (len - 1) (dcsInput dcs)-                               return (L.cons byte1 rest)-    | otherwise           = do rest <- getBlock (len - S.length bstr) S.empty-                               return (L.fromStrict bstr `L.append` rest)+    | len < S.length bstr = do+      let (mine, rest) = S.splitAt len bstr+      dcs <- get+      set dcs{dcsNextBitNo = 8, dcsInput = rest}+      return (L.fromStrict mine)+    | S.null bstr = do+      getNextChunk+      dcs <- get+      let byte1 = dcsCurByte dcs+      rest <- getBlock (len - 1) (dcsInput dcs)+      return (L.cons byte1 rest)+    | otherwise = do+      rest <- getBlock (len - S.length bstr) S.empty+      return (L.fromStrict bstr `L.append` rest) -nextCode :: Show a => HuffmanTree a -> DeflateM a-nextCode tree =-  do b <- nextBits 1-     case advanceTree b tree of-       AdvanceError str -> raise (HuffmanTreeError str)-       NewTree tree'    -> nextCode tree'-       Result x         -> return x+nextCode :: Show a => HuffmanTree a -> DeflateM s a+nextCode tree = do+  b <- nextBits 1+  case advanceTree b tree of+    AdvanceError str -> raise (HuffmanTreeError str)+    NewTree tree' -> nextCode tree'+    Result x -> return x {-# INLINE nextCode #-} -advanceToByte :: DeflateM ()-advanceToByte =-  do dcs <- get-     set dcs{ dcsNextBitNo = 8 }+advanceToByte :: DeflateM s ()+advanceToByte = do+  dcs <- get+  set dcs{dcsNextBitNo = 8} -emitByte :: Word8 -> DeflateM ()-emitByte b =-  do dcs <- get-     set dcs{ dcsOutput  = dcsOutput dcs `addByte` b-            , dcsAdler32 = advanceAdler (dcsAdler32 dcs) b }+emitByte :: Word8 -> DeflateM s ()+emitByte b = do+  dcs <- get+  output' <- liftST (addByte (dcsOutput dcs) b)+  let adler' = advanceAdler (dcsAdler32 dcs) b+  set dcs{dcsOutput = output', dcsAdler32 = adler'} {-# INLINE emitByte #-} -emitBlock :: L.ByteString -> DeflateM ()-emitBlock b =-  do dcs <- get-     set dcs { dcsOutput  = dcsOutput dcs `addChunk` b-             , dcsAdler32 = advanceAdlerBlock (dcsAdler32 dcs) b }+emitBlock :: L.ByteString -> DeflateM s ()+emitBlock b = do+  dcs <- get+  output' <- liftST (addChunk (dcsOutput dcs) b)+  let adler' = L.foldlChunks advanceAdlerBlock (dcsAdler32 dcs) b+  set dcs{dcsOutput = output', dcsAdler32 = adler'} -emitPastChunk :: Int -> Int64 -> DeflateM ()-emitPastChunk dist len =-  do dcs <- get-     let (output', newChunk) = addOldChunk (dcsOutput dcs) dist len-     set dcs { dcsOutput = output'-             , dcsAdler32 = advanceAdlerBlock (dcsAdler32 dcs) newChunk }+emitPastChunk :: Int -> Int -> DeflateM s ()+emitPastChunk dist len = do+  dcs <- get+  (output', newChunk) <- liftST (addOldChunk (dcsOutput dcs) dist len)+  set+    dcs+      { dcsOutput = output'+      , dcsAdler32 = advanceAdlerBlock (dcsAdler32 dcs) newChunk+      } {-# INLINE emitPastChunk #-} -finalAdler :: DeflateM Word32-finalAdler = (finalizeAdler . dcsAdler32) `fmap` get+finalAdler :: DeflateM s Word32+finalAdler = (finalizeAdler . dcsAdler32) <$> get -moveWindow :: DeflateM ()-moveWindow =-  do dcs <- get-     case emitExcess (dcsOutput dcs) of-       Nothing ->-         return ()-       Just (builtChunks, output') ->-         do set dcs{ dcsOutput = output' }-            publishLazy builtChunks+moveWindow :: DeflateM s ()+moveWindow = do+  dcs <- get+  possibleExcess <- liftST (emitExcess (dcsOutput dcs))+  case possibleExcess of+    Nothing ->+      return ()+    Just (builtChunk, output') -> do+      set dcs{dcsOutput = output'}+      publish builtChunk -finalize :: DeflateM ()-finalize =-  do dcs <- get-     publishLazy (finalizeWindow (dcsOutput dcs))+finalize :: DeflateM s ()+finalize = do+  dcs <- get+  lastChunk <- liftST (finalizeWindow (dcsOutput dcs))+  publish lastChunk -{-# INLINE publishLazy #-}-publishLazy :: L.ByteString -> DeflateM ()-publishLazy lbstr = DeflateM (\ st k -> Chunk lbstr (k st ()))+{-# INLINE publish #-}+publish :: S.ByteString -> DeflateM s ()+publish bstr = DeflateM $ \st k ->+  return (Chunk bstr (k st ()))
src/Codec/Compression/Zlib/OutputWindow.hs view
@@ -1,65 +1,114 @@ {-# LANGUAGE BangPatterns #-}+{-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE UnboxedTuples #-} {-# OPTIONS_GHC -fno-warn-orphans #-}-module Codec.Compression.Zlib.OutputWindow(-         OutputWindow-       , emptyWindow-       , emitExcess-       , finalizeWindow-       , addByte-       , addChunk-       , addOldChunk-       )- where -import           Data.ByteString.Builder(Builder, toLazyByteString, word8, lazyByteString)-import qualified Data.ByteString      as S+module Codec.Compression.Zlib.OutputWindow (+  OutputWindow,+  emptyWindow,+  emitExcess,+  finalizeWindow,+  addByte,+  addChunk,+  addOldChunk,+) where++import Control.Monad (foldM)+import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L-import           Codec.Compression.Zlib.FingerTree(FingerTree, empty, (|>), dropTakeCombine, split, measure, toBuilder)-import           Data.Int(Int64)-import           Data.Semigroup as Sem-import           Data.Word(Word8)-import           Prelude()-import           Prelude.Compat+import qualified Data.ByteString.Short as SBS+import Data.ByteString.Short.Internal (ShortByteString (SBS))+import qualified Data.Primitive as Prim+import qualified Data.Vector.Primitive as V+import qualified Data.Vector.Primitive.Mutable as MV+import GHC.ST (ST (..))+import GHC.Word (Word8 (..)) -type WindowType = FingerTree S.ByteString+windowSize :: Int+windowSize = 128 * 1024 -data OutputWindow = OutputWindow {-       owWindow    :: WindowType-     , owRecent    :: Builder-     }+data OutputWindow s = OutputWindow+  { owWindow :: {-# UNPACK #-} !(MV.MVector s Word8)+  , owNext :: {-# UNPACK #-} !Int+  } -emptyWindow :: OutputWindow-emptyWindow = OutputWindow empty mempty+emptyWindow :: ST s (OutputWindow s)+emptyWindow = do+  window <- MV.new windowSize+  return (OutputWindow window 0) -emitExcess :: OutputWindow -> Maybe (L.ByteString, OutputWindow)-emitExcess ow-  | totalMeasure < 65536 = Nothing-  | otherwise            = Just (excess, ow{ owWindow = window' })- where-  window              = owWindow ow-  totalMeasure        = measure window-  excessAmount        = totalMeasure - 32768-  (excessFT, window') = split excessAmount window-  excess              = toLazyByteString (toBuilder excessFT)+excessChunkSize :: Int+excessChunkSize = 32768 -finalizeWindow :: OutputWindow -> L.ByteString-finalizeWindow ow = toLazyByteString (toBuilder (owWindow ow) <> owRecent ow)+emitExcess :: OutputWindow s -> ST s (Maybe (S.ByteString, OutputWindow s))+emitExcess OutputWindow{owWindow = window, owNext = initialOffset}+  | initialOffset < excessChunkSize * 2 = return Nothing+  | otherwise = do+    toEmit <- V.freeze $ MV.slice 0 excessChunkSize window+    let excessLength = initialOffset - excessChunkSize+    -- Need move as these can overlap!+    MV.move (MV.slice 0 excessLength window) (MV.slice excessChunkSize excessLength window)+    let ow' = OutputWindow window excessLength+    return (Just (SBS.fromShort $ toByteString toEmit, ow')) +finalizeWindow :: OutputWindow s -> ST s S.ByteString+finalizeWindow ow = do+  -- safe as we're doing it at the end+  res <- V.unsafeFreeze (MV.slice 0 (owNext ow) (owWindow ow))+  pure $ SBS.fromShort $ toByteString res+ -- ----------------------------------------------------------------------------- -addByte :: OutputWindow -> Word8 -> OutputWindow-addByte !ow !b = ow{ owRecent = owRecent ow <> word8 b }+addByte :: OutputWindow s -> Word8 -> ST s (OutputWindow s)+addByte !ow !b = do+  let offset = owNext ow+  MV.write (owWindow ow) offset b+  return ow{owNext = offset + 1} -addChunk :: OutputWindow -> L.ByteString -> OutputWindow-addChunk !ow !bs = ow{ owRecent = owRecent ow <> lazyByteString bs }+addChunk :: OutputWindow s -> L.ByteString -> ST s (OutputWindow s)+addChunk !ow !bs = foldM copyChunk ow (L.toChunks bs) -addOldChunk :: OutputWindow -> Int -> Int64 -> (OutputWindow, L.ByteString)-addOldChunk !ow !dist !len = (OutputWindow output (lazyByteString chunk), chunk)+copyChunk :: OutputWindow s -> S.ByteString -> ST s (OutputWindow s)+copyChunk ow sbstr = do+  -- safe as we're never going to look at this again+  ba <- V.unsafeThaw $ fromByteString $ SBS.toShort sbstr+  let offset = owNext ow+      len = MV.length ba+  MV.copy (MV.slice offset len (owWindow ow)) ba+  return ow{owNext = offset + len}++addOldChunk :: OutputWindow s -> Int -> Int -> ST s (OutputWindow s, S.ByteString)+addOldChunk (OutputWindow window next) dist len = do+  -- zlib can ask us to copy an "old" chunk that extends past our current offset.+  -- The intention is that we then start copying the "new" data we just copied into+  -- place. 'copyChunked' handles this for us.+  copyChunked (MV.slice next len window) (MV.slice (next - dist) len window) dist+  result <- V.freeze $ MV.slice next len window+  return (OutputWindow window (next + len), SBS.fromShort $ toByteString result)++{- | A copy function that copies the buffers sequentially in chunks no larger than+ the stated size. This allows us to handle the insane zlib behaviour.+-}+copyChunked :: MV.MVector s Word8 -> MV.MVector s Word8 -> Int -> ST s ()+copyChunked dest src chunkSize = go 0 (MV.length src)  where-  output      = L.foldlChunks (|>) (owWindow ow) (toLazyByteString (owRecent ow))-  dropAmt     = measure output - dist-  (prev, sme) = split dropAmt output-  chunkBase   = dropTakeCombine (dropAmt - measure prev) (fromIntegral len) sme-  chunkInf    = chunkBase `L.append` chunkInf-  chunk       = L.take len chunkInf+  go _ 0 = pure ()+  go copied toCopy = do+    let thisChunkSize = min toCopy chunkSize+    MV.copy (MV.slice copied thisChunkSize dest) (MV.slice copied thisChunkSize src)+    go (copied + thisChunkSize) (toCopy - thisChunkSize)++-- TODO: these are a bit questionable. Maybe we can just pass around Vector Word8 in the client code?+fromByteString :: SBS.ShortByteString -> V.Vector Word8+fromByteString (SBS ba) =+  let len = Prim.sizeofByteArray (Prim.ByteArray ba)+      sz = Prim.sizeOf (undefined :: Word8)+   in V.Vector 0 (len * sz) (Prim.ByteArray ba)++toByteString :: V.Vector Word8 -> SBS.ShortByteString+toByteString (V.Vector offset len ba) =+  let sz = Prim.sizeOf (undefined :: Word8)+      !(Prim.ByteArray ba') = Prim.cloneByteArray ba (offset * sz) (len * sz)+   in SBS ba'
test/Test.hs view
@@ -1,18 +1,18 @@ import Codec.Compression.Zlib import Codec.Compression.Zlib.Deflate-import Data.ByteString.Lazy(readFile)+import Data.ByteString.Lazy (readFile) import Data.Char (ord)-import Data.List(last, isPrefixOf)-import Prelude hiding (readFile)+import Data.List (isPrefixOf) import System.FilePath import Test.Tasty import Test.Tasty.HUnit+import Prelude hiding (readFile)  -- -----------------------------------------------------------------------------  rfcSimpleTestLengths :: [(Int, Int)]-rfcSimpleTestLengths = [-    (ord 'A', 3)+rfcSimpleTestLengths =+  [ (ord 'A', 3)   , (ord 'B', 3)   , (ord 'C', 3)   , (ord 'D', 3)@@ -23,76 +23,103 @@   ]  rfcSimpleTestResults :: [(Int, Int, Int)]-rfcSimpleTestResults = [-    (ord 'A', 3, 2)  --  010-  , (ord 'B', 3, 3)  --  011-  , (ord 'C', 3, 4)  --  100-  , (ord 'D', 3, 5)  --  101-  , (ord 'E', 3, 6)  --  110-  , (ord 'F', 2, 0)  --   00+rfcSimpleTestResults =+  [ (ord 'A', 3, 2) --  010+  , (ord 'B', 3, 3) --  011+  , (ord 'C', 3, 4) --  100+  , (ord 'D', 3, 5) --  101+  , (ord 'E', 3, 6) --  110+  , (ord 'F', 2, 0) --   00   , (ord 'G', 4, 14) -- 1110   , (ord 'H', 4, 15) -- 1111   ]  fixedHuffmanLengths :: [(Int, Int)] fixedHuffmanLengths =-  ([(x, 8) | x <- [0   .. 143]] ++-   [(x, 9) | x <- [144 .. 255]] ++-   [(x, 7) | x <- [256 .. 279]] ++-   [(x, 8) | x <- [280 .. 287]])+  ( [(x, 8) | x <- [0 .. 143]]+    ++ [(x, 9) | x <- [144 .. 255]]+      ++ [(x, 7) | x <- [256 .. 279]]+      ++ [(x, 8) | x <- [280 .. 287]]+  )  fixedHuffmanResults :: [(Int, Int, Int)] fixedHuffmanResults =-  ([(fst x, 8, snd x) | x <- zip [0  ..143] [48 ..191]] ++ --  00110000 through  10111111-   [(fst x, 9, snd x) | x <- zip [144..255] [400..511]] ++ -- 110010000 through 111111111-   [(fst x, 7, snd x) | x <- zip [256..279] [0  .. 23]] ++ --   0000000 through   0010111-   [(fst x, 8, snd x) | x <- zip [280..287] [192..199]])   --  11000000 through  11000111+  ( [(fst x, 8, snd x) | x <- zip [0 .. 143] [48 .. 191]]+    ++ [(fst x, 9, snd x) | x <- zip [144 .. 255] [400 .. 511]] --  00110000 through  10111111+      ++ [(fst x, 7, snd x) | x <- zip [256 .. 279] [0 .. 23]] -- 110010000 through 111111111+      ++ [(fst x, 8, snd x) | x <- zip [280 .. 287] [192 .. 199]] --   0000000 through   0010111+      --  11000000 through  11000111+  )  -- -----------------------------------------------------------------------------  testCases :: [FilePath]-testCases = [ "randtest1", "randtest2", "randtest3",-              "rfctest1",  "rfctest2",  "rfctest3",-              "zerotest1", "zerotest2", "zerotest3" ]+testCases =+  [ "randtest1"+  , "randtest2"+  , "randtest3"+  , "rfctest1"+  , "rfctest2"+  , "rfctest3"+  , "zerotest1"+  , "zerotest2"+  , "zerotest3"+  ]  buildGoldTestCases :: IO TestTree buildGoldTestCases =-  do trees <- mapM buildGoldTest testCases-     return (testGroup "Decompression Tests" trees)+  do+    trees <- mapM buildGoldTest testCases+    return (testGroup "Decompression Tests" trees)  buildGoldTest :: FilePath -> IO TestTree buildGoldTest test =-  do let compressedFile = "test" </> "test-cases" </> test <.> "z"-         goldFile       = "test" </> "test-cases" </> test <.> "gold"-     compressedBStr <- readFile compressedFile-     goldBStr       <- readFile goldFile-     return (testCase (toTestCaseName test)-              (assertEqual test (Right goldBStr) (decompress compressedBStr)))+  do+    let compressedFile = "test" </> "test-cases" </> test <.> "z"+        goldFile = "test" </> "test-cases" </> test <.> "gold"+    compressedBStr <- readFile compressedFile+    goldBStr <- readFile goldFile+    return+      ( testCase+          (toTestCaseName test)+          (assertEqual test (Right goldBStr) (decompress compressedBStr))+      )  toTestCaseName :: FilePath -> String toTestCaseName fpath = prefix ++ suffix  where-  prefix | "zero" `isPrefixOf` fpath = "Zero test #"-         | "rand" `isPrefixOf` fpath = "Random test #"-         | "rfc"  `isPrefixOf` fpath = "RFC test #"-         | otherwise                 = error "Bad test case prefix."+  prefix+    | "zero" `isPrefixOf` fpath = "Zero test #"+    | "rand" `isPrefixOf` fpath = "Random test #"+    | "rfc" `isPrefixOf` fpath = "RFC test #"+    | otherwise = error "Bad test case prefix."   suffix = [last fpath]  -- -----------------------------------------------------------------------------  zlibTests :: IO TestTree zlibTests =-  do decompTests <- buildGoldTestCases-     return $ testGroup "DEFLATE / ZLib Algorithm Testing" [-                  testCase "RFC 1951 Code Generation Test"-                    (assertEqual "" (computeCodeValues rfcSimpleTestLengths)-                                    rfcSimpleTestResults)-                , testCase "Fixed Huffman lengths make right tree"-                    (assertEqual "" (computeCodeValues fixedHuffmanLengths)-                                    fixedHuffmanResults)-                , decompTests-                ]+  do+    decompTests <- buildGoldTestCases+    return $+      testGroup+        "DEFLATE / ZLib Algorithm Testing"+        [ testCase+            "RFC 1951 Code Generation Test"+            ( assertEqual+                ""+                (computeCodeValues rfcSimpleTestLengths)+                rfcSimpleTestResults+            )+        , testCase+            "Fixed Huffman lengths make right tree"+            ( assertEqual+                ""+                (computeCodeValues fixedHuffmanLengths)+                fixedHuffmanResults+            )+        , decompTests+        ]  main :: IO () main = defaultMain =<< zlibTests-
− test/test-cases/bytes-tl.gold

file too large to diff