------------------------------------------------------------------------------- |-- Copyright : (c) 2006-2007 Duncan Coutts-- License : BSD-style---- Maintainer : duncan.coutts@worc.ox.ac.uk-- Stability : provisional-- Portability : portable (H98 + FFI)---- Pure stream based interface to lower level bzlib wrapper-------------------------------------------------------------------------------moduleCodec.Compression.BZip.Internal(-- * Compression and decompressioncompressDefault,decompressDefault,Stream.BlockSize(..),-- * The same but with the full set of parameterscompressFull,decompressFull,Stream.WorkFactor(..),Stream.MemoryLevel(..),Stream.Verbosity(..),)whereimportPreludehiding(length)importControl.Monad(when)importControl.Exception(assert)importqualifiedData.ByteString.LazyasL#ifdef BYTESTRING_IN_BASEimportqualifiedData.ByteString.BaseasS#elseimportqualifiedData.ByteString.Lazy.InternalasLimportqualifiedData.ByteString.InternalasS#endifimportqualifiedCodec.Compression.BZip.StreamasStreamimportCodec.Compression.BZip.Stream(Stream)compressDefault::Stream.BlockSize->L.ByteString->L.ByteStringcompressDefaultblockSize=compressFullblockSizeStream.SilentStream.DefaultWorkFactordecompressDefault::L.ByteString->L.ByteStringdecompressDefault=decompressFullStream.SilentStream.DefaultMemoryLevel{-# NOINLINE compressFull #-}compressFull::Stream.BlockSize->Stream.Verbosity->Stream.WorkFactor->L.ByteString->L.ByteStringcompressFullblockSizeverbosityworkFactorinput=L.fromChunks$Stream.run$doStream.compressInitblockSizeverbosityworkFactorcaseL.toChunksinputof[]->fillBuffers[]S.PSinFPtroffsetlength:chunks->doStream.pushInputBufferinFPtroffsetlengthfillBufferschunkswhere#ifdef BYTESTRING_IN_BASEoutChunkSize=16*1024-16#elseoutChunkSize=32*1024-L.chunkOverhead#endif-- we flick between two states:-- * where one or other buffer is empty-- - in which case we refill one or both-- * where both buffers are non-empty-- - in which case we compress until a buffer is emptyfillBuffers::[S.ByteString]->Stream[S.ByteString]fillBuffersinChunks=doStream.consistencyCheck-- in this state there are two possabilities:-- * no outbut buffer space is available-- - in which case we must make more available-- * no input buffer is available-- - in which case we must supply moreinputBufferEmpty<-Stream.inputBufferEmptyoutputBufferFull<-Stream.outputBufferFullassert(inputBufferEmpty||outputBufferFull)$return()whenoutputBufferFull$dooutFPtr<-Stream.unsafeLiftIO(S.mallocByteStringoutChunkSize)Stream.pushOutputBufferoutFPtr0outChunkSizeifinputBufferEmptythencaseinChunksof[]->drainBuffers[]S.PSinFPtroffsetlength:inChunks'->doStream.pushInputBufferinFPtroffsetlengthdrainBuffersinChunks'elsedrainBuffersinChunksdrainBuffers::[S.ByteString]->Stream[S.ByteString]drainBuffersinChunks=doinputBufferEmpty'<-Stream.inputBufferEmptyoutputBufferFull'<-Stream.outputBufferFullassert(notoutputBufferFull'&&(nullinChunks||notinputBufferEmpty'))$return()-- this invariant guarantees we can always make forward progressletaction=ifnullinChunksthenStream.FinishelseStream.Runstatus<-Stream.compressactioncasestatusofStream.Ok->dooutputBufferFull<-Stream.outputBufferFullifoutputBufferFullthendo(outFPtr,offset,length)<-Stream.popOutputBufferoutChunks<-Stream.unsafeInterleave(fillBuffersinChunks)return(S.PSoutFPtroffsetlength:outChunks)elsedofillBuffersinChunksStream.StreamEnd->doinputBufferEmpty<-Stream.inputBufferEmptyassertinputBufferEmpty$return()outputBufferBytesAvailable<-Stream.outputBufferBytesAvailableifoutputBufferBytesAvailable>0thendo(outFPtr,offset,length)<-Stream.popOutputBufferStream.finalisereturn[S.PSoutFPtroffsetlength]elsedoStream.finalisereturn[]{-# NOINLINE decompressFull #-}decompressFull::Stream.Verbosity->Stream.MemoryLevel->L.ByteString->L.ByteStringdecompressFullverbositymemLevelinput=L.fromChunks$Stream.run$doStream.decompressInitverbositymemLevelcaseL.toChunksinputof[]->fillBuffers[]S.PSinFPtroffsetlength:chunks->doStream.pushInputBufferinFPtroffsetlengthfillBufferschunkswhereoutChunkSize::Int#ifdef BYTESTRING_IN_BASEoutChunkSize=16*1024-16#elseoutChunkSize=32*1024-L.chunkOverhead#endif-- we flick between two states:-- * where one or other buffer is empty-- - in which case we refill one or both-- * where both buffers are non-empty-- - in which case we compress until a buffer is emptyfillBuffers::[S.ByteString]->Stream[S.ByteString]fillBuffersinChunks=do-- in this state there are two possabilities:-- * no outbut buffer space is available-- - in which case we must make more available-- * no input buffer is available-- - in which case we must supply moreinputBufferEmpty<-Stream.inputBufferEmptyoutputBufferFull<-Stream.outputBufferFullassert(inputBufferEmpty||outputBufferFull)$return()whenoutputBufferFull$dooutFPtr<-Stream.unsafeLiftIO(S.mallocByteStringoutChunkSize)Stream.pushOutputBufferoutFPtr0outChunkSizeifinputBufferEmptythencaseinChunksof[]->drainBuffers[]S.PSinFPtroffsetlength:inChunks'->doStream.pushInputBufferinFPtroffsetlengthdrainBuffersinChunks'elsedrainBuffersinChunksdrainBuffers::[S.ByteString]->Stream[S.ByteString]drainBuffersinChunks=doinputBufferEmpty'<-Stream.inputBufferEmptyoutputBufferFull'<-Stream.outputBufferFullassert(notoutputBufferFull'&&(nullinChunks||notinputBufferEmpty'))$return()-- this invariant guarantees we can always make forward progress or at-- least detect premature EOFstatus<-Stream.decompresscasestatusofStream.Ok->dooutputBufferFull<-Stream.outputBufferFullifoutputBufferFullthendo(outFPtr,offset,length)<-Stream.popOutputBufferoutChunks<-Stream.unsafeInterleave(fillBuffersinChunks)return(S.PSoutFPtroffsetlength:outChunks)elsedo-- We need to detect if we ran out of input:inputBufferEmpty<-Stream.inputBufferEmptyifinputBufferEmpty&&nullinChunksthenfail"premature end of compressed stream"elsefillBuffersinChunksStream.StreamEnd->do-- Note that there may be input bytes still available if the stream-- is embeded in some other data stream. Here we just silently discard-- any trailing data.outputBufferBytesAvailable<-Stream.outputBufferBytesAvailableifoutputBufferBytesAvailable>0thendo(outFPtr,offset,length)<-Stream.popOutputBufferStream.finalisereturn[S.PSoutFPtroffsetlength]elsedoStream.finalisereturn[]