pdf-toolbox-core-0.0.3.3: lib/Pdf/Toolbox/Core/IO/RIS.hs
-- | Input stream with random access
module Pdf.Toolbox.Core.IO.RIS
(
IS,
RIS(..),
RIS'(..),
seek,
size,
tell,
inputStream,
fromHandle,
fromHandle'
)
where
import Data.Int (Int64)
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.IORef
import System.IO
import System.IO.Streams (InputStream)
import qualified System.IO.Streams as Streams
-- | Sequential input stream
type IS = InputStream ByteString
-- | Internal state of 'RIS'
data RIS' = RIS' {
risSeek :: Int64 -> IO (IO (Maybe ByteString)),
risInputStream :: IS,
risPos :: IO Int64,
risSize :: Int64
}
-- | Random access Input Stream
newtype RIS = RIS (IORef RIS')
-- | Seek the stream
seek :: RIS -> Int64 -> IO ()
seek (RIS ref) pos = do
ris <- readIORef ref
source <- risSeek ris pos
stream <- Streams.makeInputStream source
(s, c) <- Streams.countInput stream
writeIORef ref ris {
risInputStream = s,
risPos = (+ pos) <$> c
}
-- | Create RIS from 'Handle' with the specified chunk size
fromHandle' :: Handle -> Int -> IO RIS
fromHandle' h buf = do
sz <- hFileSize h
posRef <- newIORef 0
stream <- Streams.makeInputStream (f posRef)
(s, c) <- Streams.countInput stream
RIS <$> newIORef RIS' {
risSeek = \pos -> do
hSeek h AbsoluteSeek (fromIntegral pos)
ref <- newIORef $ fromIntegral pos
return $ f ref,
risInputStream = s,
risPos = c,
risSize = fromIntegral sz
}
where
f ref = do
prevPos <- readIORef ref
curtPos <- hTell h
hSeek h AbsoluteSeek prevPos
chunk <- BS.hGetSome h buf
hSeek h AbsoluteSeek curtPos
writeIORef ref $! prevPos + fromIntegral (BS.length chunk)
return $! if BS.null chunk then Nothing else Just chunk
-- | Create RIS from 'Handle' with default chunk size
fromHandle :: Handle -> IO RIS
fromHandle h = fromHandle' h 32752
-- | Number of bytes in the stream
size :: RIS -> IO Int64
size (RIS ref) = risSize <$> readIORef ref
-- | Current position in bytes
tell :: RIS -> IO Int64
tell (RIS ref) = readIORef ref >>= risPos
-- | Get sequential input stream, that is valid until the next 'seek'
inputStream :: RIS -> IO IS
inputStream (RIS ref) = risInputStream <$> readIORef ref