{-# LANGUAGE BangPatterns, ScopedTypeVariables, FlexibleContexts #-}moduleData.MutableIter.IOBuffer(IOBuffer,createIOBuffer,null,empty,copyBuffer,append,length,pop,lookAtHead,drop,dropWhile,take,splitAt,mapBuffer,mapAccumBuffer,foldl',castBuffer,freeze,thaw,hPut,unsafeToForeignPtr)whereimportPreludehiding(length,take,drop,null,splitAt,dropWhile)importqualifiedData.Vector.GenericasGimportqualifiedData.IterateeasIimportControl.MonadimportControl.Monad.CatchIOimportForeignimportSystem.IOimportSystem.IO.Unsafe(unsafePerformIO)-- |A mutable buffer to hold storable elements. This data type supports-- memory recycling.dataIOBufferrel=IOBuffer{-# UNPACK #-}!Int{-# UNPACK #-}!(ForeignPtrInt){-# UNPACK #-}!(ForeignPtrel)instanceStorableel=>I.NullPoint(IOBufferrel)whereempty=emptyfpPeek::Storableel=>ForeignPtrel->IOelfpPeek=flipwithForeignPtrpeekfpPoke::Storableel=>ForeignPtrel->el->IO()fpPokefpel=withForeignPtrfp(flippokeel)withBuf::IOBufferrel->(Int->PtrInt->Ptrel->IOa)->IOawithBuf(IOBufferlofpofb)f=withForeignPtrofp(\op->withForeignPtrofb(\ob->flopob)){-# INLINE withBuf #-}newFp::Storablea=>a->IO(ForeignPtra)newFpa=mallocForeignPtr>>=\fp->fpPokefpa>>returnfp-- |Create a buffer from a length and data array.createIOBuffer::(Storableel)=>Int->ForeignPtrInt->ForeignPtrel->IOBufferrelcreateIOBufferlenopbuf=IOBufferlenopbuf-- |Empty buffer.empty::Storableel=>IOBufferrelempty=IOBuffer0nullForeignPtrnullForeignPtrnullForeignPtr=unsafePerformIO(newForeignPtr_nullPtr)-- |Check if the buffer is empty.null::IOBufferrel->IOBoolnull(IOBuffer0__)=returnTruenullbuf=withBufbuf$\lpo_->liftM(>=l)$peekpo{-# INLINE null #-}-- |IOBuffer length.length::IOBufferrel->IOIntlengthbuf=withBufbuf$\lpo_->liftM(l-)$peekpo{-# INLINE length #-}-- |Retrieve the front element from the buffer and advance the internal pointer.-- It is an error to call this on an empty buffer.pop::(Storableel)=>IOBufferrel->IOelpop(IOBuffer0__)=error"Can't pop head off of empty buffer"popbuf=withBufbuf$\lpopb->dooff<-peekpoifoff>=lthenerror"Can't pop head from empty buffer"elsepokepo(off+1)>>peek(pb`advancePtr`off){-# INLINE pop #-}-- |Retrieve the first element, if it exists.-- This function does not advance the buffer pointer.lookAtHead::(Storableel)=>IOBufferrel->IO(Maybeel)lookAtHead(IOBuffer0__)=returnNothinglookAtHeadbuf=withBufbuf$\lpopb->dooff<-peekpoifoff>=lthenreturnNothingelseliftMJust$peek(pb`advancePtr`off){-# INLINE lookAtHead #-}-- |Drop n elements from the front of the buffer.-- if the buffer has fewer elements, all are dropped.drop::Int->IOBufferrel->IO()dropn_dropbuf=withBufbuf$\lpopb->dooff<-peekpopokepo(minl(off+n_drop)){-# INLINE drop #-}dropWhile::(Storableel)=>(el->Bool)->IOBufferrel->IO()dropWhilepredbuf=withBufbuf$\lpopb->dooff<-peekpoletlen=l-offletgocntp|cnt<len=dothis<-peekpifpredthisthengo(succcnt)(p`advancePtr`1)elsereturncntletgocnt_=returncnt--off the end of the buffern<-go0(pb`advancePtr`off)pokepon{-# INLINE dropWhile #-}-- |Create a new buffer from the first n elements, sharing data.-- This function advances the pointer of the original buffer.take::(Storableel)=>IOBufferrel->Int->IO(IOBufferrel)take(IOBuffer0__)_=returnemptytakebuf@(IOBuffer_fpofpb)n_take=withBufbuf$\lpopb->dooff<-peekpopo'<-newFpoffpokepo$minl(off+n_take)return$IOBufferlpo'fpb-- |Split one buffer to two, sharing storage.splitAt::(Storableel)=>IOBufferrel->Int->IO(IOBufferrel,IOBufferrel)splitAt(IOBuffer0__)_=return(empty,empty)splitAtbuf0=return(empty,buf)splitAtbuf@(IOBufferlfpofpb)n|n>0&&n<=l=withForeignPtrfpo$\po->withForeignPtrfpb$\pb->doletib1=IOBuffernfpofpboff<-peekpopo2<-newFp(off+n)return(ib1,IOBufferlpo2fpb)|True=return(buf,empty)-- |Copy data from one buffer to another.copyBuffer::(Storableel)=>IOBufferrel->IO(IOBufferrel)copyBuffer(IOBuffer0__)=returnemptycopyBufferbuf=withBufbuf$\lpopb->dooff<-peekpoletlen'=l-offiflen'>0thendopo'<-newFp0pb'<-mallocForeignPtrArraylen'withForeignPtrpb'$\p->copyArrayp(pb`advancePtr`off)len'return$IOBufferlen'po'pb'elsereturnempty-- |Append two buffers. Copies data from both into a new buffer.append::(Storableel)=>IOBufferrel->IOBufferrel->IO(IOBufferrel)appendib1(IOBuffer0__)=copyBufferib1append(IOBuffer0__)ib2=copyBufferib2appendib1ib2=withBufib1(\l1po1pb1->withBufib2(\l2po2pb2->dolen1<-lengthib1len2<-lengthib2letlen=len1+len2off1<-peekpo1off2<-peekpo2po<-newFp0pb<-mallocForeignPtrArraylenwithForeignPtrpb$\p->copyArrayp(pb1`advancePtr`off1)len1withForeignPtrpb$\p->copyArray(p`advancePtr`len1)(pb2`advancePtr`off2)len2return$IOBufferlenpopb))-- |Safely convert an IOBuffer to a Vector.freeze::(Storableel,G.Vectorvel,G.VectorvInt)=>IOBufferrel->IO(vel)freeze(IOBuffer0__)=returnG.emptyfreezebuf=withBufbuf$\lpopb->dolen<-lengthbufoff<-peekpoletvIx=G.enumFromN0lenvRes<-G.forMvIx(\ix->peekElemOff(pb`advancePtr`off)ix)returnvRes{-# INLINE freeze #-}-- |Safely convert a Vector to an IOBufferthaw::(Storableel,G.Vectorvel)=>vel->IO(IOBufferrel)thawvec=dooffp<-newFp0bufp<-mallocForeignPtrArray(G.lengthvec)withForeignPtrbufp$\p->G.foldM'(\ixel->pokeElemOffpixel>>return(ix+1))0vecreturn$createIOBuffer(G.lengthvec)offpbufp{-# INLINE thaw #-}-- |Write out the contents of the IOBuffer to a handle. This operation-- drains the buffer.hPut::forallrel.(Storableel)=>Handle->IOBufferrel->IO()hPuth(IOBuffer0__)=return()hPuthbuf=withBufbuf$\lpopb->dooff<-peekpo(hPutBufh)(pb`advancePtr`off)(bytemult*(l-off))pokepolwherebytemult=sizeOf(undefined::el)-- |copy data from one buffer to another with the specified map function.-- this operation drains the original buffer.mapBuffer::(Storableel,Storableel')=>(el->el')->ForeignPtrInt->ForeignPtrel'->IOBufferrel->IO(IOBufferrel')mapBufferfdpdatap(IOBuffer0__)=returnemptymapBufferffdpfdatapbuf=withForeignPtrfdp$\dp->withForeignPtrfdatap$\datap->withBufbuf$\lpopb->letgo!cntip'op'=when(cnt>0)$dopokeop'.f=<<peekip'go(cnt-1)(ip'`advancePtr`1)(op'`advancePtr`1)indooff<-peekpogo(l-off)(pb`advancePtr`off)datappokedp0pokepolreturn$createIOBuffer(l-off)fdpfdatap{-# INLINE mapBuffer #-}mapAccumBuffer::(Storableel,Storableel')=>(acc->el->(acc,el'))->ForeignPtrInt->ForeignPtrel'->acc->IOBufferrel->IO(acc,IOBufferrel')mapAccumBufferffdpfdatapacc(IOBuffer0__)=return(acc,empty)mapAccumBufferffdpfdatapaccbuf=withForeignPtrfdp$\dp->withForeignPtrfdatap$\datap->withBufbuf$\lpopb->letgo!acc'cnt!ip'!op'|cnt==0=returnacc'|True=do(acc2,el)<-liftM(facc')$peekip'pokeop'elgoacc2(cnt-1)(ip'`advancePtr`1)(op'`advancePtr`1)indooff<-peekpoacc'<-goacc(l-off)(pb`advancePtr`off)datappokedp0pokepolreturn$(acc',createIOBuffer(l-off)fdpfdatap){-# INLINE mapAccumBuffer #-}-- |Cast a buffer to a different type. Any extra data is truncated.-- This is not safe unless the buffer offset is 0.castBuffer::forallrmelel'.(Storableel,Storableel')=>IOBufferrel->IO(IOBufferrel')castBuffer(IOBuffer0__)=returnemptycastBuffer(IOBufferlpopb)=dooff<-fpPeekpowhen(off/=0)(error"castBuffer called with non-zero offset")return$IOBufferl'po(castForeignPtrpb)wherel'=(l*sizeOf(undefined::el))`div`sizeOf(undefined::el')foldl'::(Storableb)=>(a->b->a)->a->IOBufferrb->IOafoldl'facc(IOBuffer0__)=returnaccfoldl'fi0buf=withBufbuf$\lpopb->letgo!n!accp|n>0=doel<-peekpgo(n-1)(faccel)(p`advancePtr`1)go_acc_=returnaccindooff<-peekpogo(l-off)i0(pb`advancePtr`off){-# INLINE foldl' #-}unsafeToForeignPtr::IOBufferrel->(Int,ForeignPtrInt,ForeignPtrel)unsafeToForeignPtr(IOBufferlpopb)=(l,po,pb)