bytepatch-0.3.0: src/StreamPatch/Stream.hs
module StreamPatch.Stream where
import StreamPatch.Patch
import Util
import Data.Vinyl
import GHC.Natural
import Data.Kind
import qualified Data.ByteString as BS
import qualified Data.ByteString.Builder as BB
import qualified Data.ByteString.Lazy as BL
import Control.Monad.State
import Control.Monad.Reader
import System.IO ( Handle, SeekMode(..), hSeek )
import qualified Data.List as List
-- TODO also require reporting cursor position (for error reporting)
class Monad m => MonadFwdStream m where
type Chunk m :: Type
-- | Read a number of bytes without advancing the cursor.
readahead :: Natural -> m (Chunk m)
-- | Advance cursor without reading.
advance :: Natural -> m ()
-- | Insert bytes into the stream at the cursor position, overwriting
-- existing bytes.
overwrite :: Chunk m -> m ()
-- TODO Need MonoTraversable to define for Text, ByteString etc easily. Bleh. I
-- think Snoyman's advice is to reimplement. Also bleh.
instance Monad m => MonadFwdStream (StateT ([a], [a]) m) where
type Chunk (StateT ([a], [a]) m) = [a]
readahead n = List.take (fromIntegral n) <$> gets fst
advance n = do
(src, out) <- get
let (bs, src') = List.splitAt (fromIntegral n) src
put (src', out <> bs)
overwrite bs = do
(src, out) <- get
let (_, src') = List.splitAt (List.length bs) src
put (src', out <> bs)
instance MonadIO m => MonadFwdStream (ReaderT Handle m) where
type Chunk (ReaderT Handle m) = BS.ByteString
readahead n = do
hdl <- ask
bs <- liftIO $ BS.hGet hdl (fromIntegral n)
liftIO $ hSeek hdl RelativeSeek (- fromIntegral n)
return bs
advance n = do
hdl <- ask
liftIO $ hSeek hdl RelativeSeek (fromIntegral n)
overwrite bs = do
hdl <- ask
liftIO $ BS.hPut hdl bs
instance Monad m => MonadFwdStream (StateT (BS.ByteString, BB.Builder) m) where
type Chunk (StateT (BS.ByteString, BB.Builder) m) = BS.ByteString
readahead n = BS.take (fromIntegral n) <$> gets fst
advance n = do
(src, out) <- get
let (bs, src') = BS.splitAt (fromIntegral n) src
put (src', out <> BB.byteString bs)
overwrite bs = do
(src, out) <- get
let (_, src') = BS.splitAt (BS.length bs) src
put (src', out <> BB.byteString bs)
-- | A forward stream, but backward too.
class MonadFwdStream m => MonadCursorStream m where
-- | Move cursor.
move :: Integer -> m ()
instance MonadIO m => MonadCursorStream (ReaderT Handle m) where
move n = do
hdl <- ask
liftIO $ hSeek hdl RelativeSeek (fromIntegral n)