libarchive-1.0.5.0: src/Codec/Archive/Unpack/Lazy.hs
module Codec.Archive.Unpack.Lazy ( readArchiveBSL
, unpackToDirLazy
) where
import Codec.Archive.Common
import Codec.Archive.Foreign
import Codec.Archive.Types
import Codec.Archive.Unpack
import Control.Monad (void, (<=<))
import Data.ByteString (useAsCStringLen)
import qualified Data.ByteString.Lazy as BSL
import Data.Functor (($>))
import Data.IORef (modifyIORef, newIORef, readIORef)
import Foreign.C.Types
import Foreign.Marshal.Alloc (free, mallocBytes)
import Foreign.Ptr
import Foreign.Storable (poke)
import System.IO.Unsafe (unsafePerformIO)
foreign import ccall memcpy :: Ptr a -- ^ Destination
-> Ptr b -- ^ Source
-> CSize -- ^ Size
-> IO (Ptr a) -- ^ Pointer to destination
-- | In general, this will be more efficient than 'unpackToDir'
--
-- @since 1.0.4.0
unpackToDirLazy :: FilePath -- ^ Directory to unpack in
-> BSL.ByteString -- ^ 'BSL.ByteString' containing archive
-> IO ()
unpackToDirLazy fp bs = do
(a, act) <- bslToArchive bs
unpackEntriesFp a fp
void $ archive_read_free a
act
-- | Read an archive lazily. The format of the archive is automatically
-- detected.
--
-- In general, this will be more efficient than 'readArchiveBS'
--
-- @since 1.0.4.0
readArchiveBSL :: BSL.ByteString -> [Entry]
readArchiveBSL = unsafePerformIO . (actFreeCallback hsEntries <=< bslToArchive)
-- | Lazily stream a 'BSL.ByteString'
-- @since 1.0.4.0
bslToArchive :: BSL.ByteString
-> IO (Ptr Archive, IO ()) -- ^ Returns an 'IO' action to be used to clean up after we're done with the archive
bslToArchive bs = do
a <- archive_read_new
void $ archive_read_support_format_all a
bufPtr <- mallocBytes (32 * 1024) -- default to 32k byte chunks; should really do something more rigorous
bsChunksRef <- newIORef bsChunks
rc <- mkReadCallback (readBSL bsChunksRef bufPtr)
cc <- mkCloseCallback (\_ ptr -> freeHaskellFunPtr rc *> free ptr $> archiveOk)
nothingPtr <- mallocBytes 0
sequence_ [ archive_read_set_read_callback a rc
, archive_read_set_close_callback a cc
, archive_read_set_callback_data a nothingPtr
, archive_read_open1 a
]
pure (a, freeHaskellFunPtr cc *> free bufPtr)
where readBSL bsRef bufPtr _ _ dataPtr = do
bs' <- readIORef bsRef
case bs' of
[] -> pure 0
(x:_) -> do
modifyIORef bsRef tail
useAsCStringLen x $ \(charPtr, sz) -> do
void $ memcpy bufPtr charPtr (fromIntegral sz)
poke dataPtr bufPtr $> fromIntegral sz
bsChunks = BSL.toChunks bs