{-# LANGUAGE BangPatterns #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# 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, byteString) import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L import Data.FingerTree(FingerTree, Measured, ViewL(..), empty, (|>), split, measure, viewl) import Data.Foldable.Compat(foldMap) import Data.Int(Int64) import Data.Semigroup as Sem import Data.Word(Word8) import Prelude() import Prelude.Compat type WindowType = FingerTree Int S.ByteString instance Sem.Semigroup Int where (<>) = (+) instance Monoid Int where mempty = 0 {-# INLINE mempty #-} mappend = (+) {-# INLINE mappend #-} instance Measured Int S.ByteString where measure = S.length {-# INLINE measure #-} data OutputWindow = OutputWindow { owWindow :: WindowType , owRecent :: Builder } emptyWindow :: OutputWindow emptyWindow = OutputWindow empty mempty 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 (foldMap byteString excessFT) finalizeWindow :: OutputWindow -> L.ByteString finalizeWindow ow = toLazyByteString (foldMap byteString (owWindow ow) <> owRecent ow) -- ----------------------------------------------------------------------------- addByte :: OutputWindow -> Word8 -> OutputWindow addByte ow b = ow{ owRecent = owRecent ow <> word8 b } addChunk :: OutputWindow -> L.ByteString -> OutputWindow addChunk ow bs = ow{ owRecent = owRecent ow <> lazyByteString bs } addOldChunk :: OutputWindow -> Int -> Int64 -> (OutputWindow, L.ByteString) addOldChunk ow dist len = (OutputWindow output (lazyByteString chunk), chunk) where output = L.foldlChunks (|>) (owWindow ow) (toLazyByteString (owRecent ow)) dropAmt = measure output - dist (prev, sme) = split (> dropAmt) output s :< rest = viewl sme start = S.take (fromIntegral len) (S.drop (dropAmt-measure prev) s) len' = fromIntegral len - S.length start chunkBase = getChunk rest len' (byteString start) chunkInf = chunkBase `L.append` chunkInf chunk = L.take len chunkInf getChunk :: WindowType -> Int -> Builder -> L.ByteString getChunk win len acc | len <= 0 = toLazyByteString acc | otherwise = case viewl win of EmptyL -> toLazyByteString acc cur :< rest -> let curlen = S.length cur in case compare (S.length cur) len of LT -> getChunk rest (len - curlen) (acc <> byteString cur) EQ -> toLazyByteString (acc <> byteString cur) GT -> let (mine, _notMine) = S.splitAt len cur in toLazyByteString (acc <> byteString mine)