module Data.ByteString.Streaming.Internal (
ByteString (..)
, consChunk
, chunkOverhead
, defaultChunkSize
, materialize
, dematerialize
, foldrChunks
, foldlChunks
, foldrChunksM
, foldlChunksM
, unfoldMChunks
, unfoldrChunks
, packChars
, smallChunkSize
, unpackBytes
, packBytes
, chunk
, mwrap
, unfoldrNE
, reread
, inlinePerformIO
, unsafeLast
, copy
, bracketByteString
) where
import Prelude hiding
(reverse,head,tail,last,init,null,length,map,lines,foldl,foldr,unlines
,concat,any,take,drop,splitAt,takeWhile,dropWhile,span,break,elem,filter,maximum
,minimum,all,concatMap,foldl1,foldr1,scanl, scanl1, scanr, scanr1
,repeat, cycle, interact, iterate,readFile,writeFile,appendFile,replicate
,getContents,getLine,putStr,putStrLn ,zip,zipWith,unzip,notElem)
import qualified Prelude
import Control.Monad.Trans
import Control.Monad
import Control.Applicative
import Control.Monad.Morph
import Data.Monoid (Monoid(..))
import qualified Data.ByteString as S
import qualified Data.ByteString.Internal as S
import Streaming (Of(..))
import Streaming.Internal hiding (concats, mwrap, step)
import qualified Streaming.Prelude as SP
import Foreign.ForeignPtr (withForeignPtr)
import Foreign.Ptr
import Foreign.Storable
import GHC.Exts ( SpecConstrAnnotation(..) )
import Data.String
import Data.Functor.Identity
import Data.Word
import System.IO.Unsafe
import GHC.Base (realWorld#,unsafeChr)
import GHC.IO (IO(IO))
import Control.Monad.Base
import Control.Monad.Trans.Resource
import Control.Monad.Catch (MonadCatch (..))
data ByteString m r =
Empty r
| Chunk !S.ByteString (ByteString m r )
| Go (m (ByteString m r ))
instance Monad m => Functor (ByteString m) where
fmap f x = case x of
Empty a -> Empty (f a)
Chunk bs bss -> Chunk bs (fmap f bss)
Go mbss -> Go (liftM (fmap f) mbss)
instance Monad m => Applicative (ByteString m) where
pure = Empty
(<*>) = ap
instance Monad m => Monad (ByteString m) where
return = Empty
x0 >> y = loop SPEC x0 where
loop !_ x = case x of
Empty _ -> y
Chunk a b -> Chunk a (loop SPEC b)
Go m -> Go (liftM (loop SPEC) m)
x >>= f =
loop SPEC2 x where
loop !_ y = case y of
Empty a -> f a
Chunk bs bss -> Chunk bs (loop SPEC bss)
Go mbss -> Go (liftM (loop SPEC) mbss)
instance MonadIO m => MonadIO (ByteString m) where
liftIO io = Go (liftM Empty (liftIO io))
instance MonadTrans ByteString where
lift ma = Go $ liftM Empty ma
instance MFunctor ByteString where
hoist phi bs = case bs of
Empty r -> Empty r
Chunk bs' rest -> Chunk bs' (hoist phi rest)
Go m -> Go (phi (liftM (hoist phi) m))
instance (r ~ ()) => IsString (ByteString m r) where
fromString = chunk . S.pack . Prelude.map S.c2w
instance (m ~ Identity, Show r) => Show (ByteString m r) where
show bs0 = case bs0 of
Empty r -> "Empty (" ++ show r ++ ")"
Go (Identity bs') -> "Go (Identity (" ++ show bs' ++ "))"
Chunk bs'' bs -> "Chunk " ++ show bs'' ++ " (" ++ show bs ++ ")"
instance (Monoid r, Monad m) => Monoid (ByteString m r) where
mempty = Empty mempty
mappend = liftM2 mappend
instance (MonadBase b m) => MonadBase b (ByteString m) where
liftBase = mwrap . fmap return . liftBase
instance (MonadThrow m) => MonadThrow (ByteString m) where
throwM = lift . throwM
instance (MonadCatch m) => MonadCatch (ByteString m) where
catch str f = go str
where
go p = case p of
Chunk bs rest -> Chunk bs (go rest)
Empty r -> Empty r
Go m -> Go (catch (do
p' <- m
return (go p'))
(\e -> return (f e)) )
instance (MonadResource m) => MonadResource (ByteString m) where
liftResourceT = lift . liftResourceT
bracketByteString :: (MonadResource m) =>
IO a -> (a -> IO ()) -> (a -> ByteString m b) -> ByteString m b
bracketByteString alloc free inside = do
(key, seed) <- lift (allocate alloc free)
clean key (inside seed)
where
clean key = loop where
loop str = case str of
Empty r -> Go (release key >> return (Empty r))
Go m -> Go (liftM loop m)
Chunk bs rest -> Chunk bs (loop rest)
data SPEC = SPEC | SPEC2
consChunk :: S.ByteString -> ByteString m r -> ByteString m r
consChunk c@(S.PS _ _ len) cs
| len == 0 = cs
| otherwise = Chunk c cs
chunk :: S.ByteString -> ByteString m ()
chunk bs = consChunk bs (Empty ())
mwrap :: m (ByteString m r) -> ByteString m r
mwrap = Go
materialize :: (forall x . (r -> x) -> (S.ByteString -> x -> x) -> (m x -> x) -> x)
-> ByteString m r
materialize phi = phi Empty Chunk Go
dematerialize :: Monad m
=> ByteString m r
-> (forall x . (r -> x) -> (S.ByteString -> x -> x) -> (m x -> x) -> x)
dematerialize x0 nil cons mwrap = loop SPEC x0
where
loop !_ x = case x of
Empty r -> nil r
Chunk b bs -> cons b (loop SPEC bs )
Go ms -> mwrap (liftM (loop SPEC) ms)
defaultChunkSize :: Int
defaultChunkSize = 32 * k chunkOverhead
where k = 1024
smallChunkSize :: Int
smallChunkSize = 4 * k chunkOverhead
where k = 1024
chunkOverhead :: Int
chunkOverhead = 2 * sizeOf (undefined :: Int)
packBytes :: Monad m => Stream (Of Word8) m r -> ByteString m r
packBytes cs0 = do
(bytes :> rest) <- lift $ SP.toList $ SP.splitAt 32 cs0
case bytes of
[] -> case rest of
Return r -> Empty r
Step as -> packBytes (Step as)
Effect m -> Go $ liftM packBytes m
_ -> Chunk (S.packBytes bytes) (packBytes rest)
packChars :: Monad m => Stream (Of Char) m r -> ByteString m r
packChars = packBytes . SP.map S.c2w
unpackBytes :: Monad m => ByteString m r -> Stream (Of Word8) m r
unpackBytes bss = dematerialize bss
Return
unpackAppendBytesLazy
Effect
where
unpackAppendBytesLazy :: S.ByteString -> Stream (Of Word8) m r -> Stream (Of Word8) m r
unpackAppendBytesLazy (S.PS fp off len) xs
| len <= 100 = unpackAppendBytesStrict (S.PS fp off len) xs
| otherwise = unpackAppendBytesStrict (S.PS fp off 100) remainder
where
remainder = unpackAppendBytesLazy (S.PS fp (off+100) (len100)) xs
unpackAppendBytesStrict :: S.ByteString -> Stream (Of Word8) m r -> Stream (Of Word8) m r
unpackAppendBytesStrict (S.PS fp off len) xs =
inlinePerformIO $ withForeignPtr fp $ \base -> do
loop (base `plusPtr` (off1)) (base `plusPtr` (off1+len)) xs
where
accursedUnutterablePerformIO (IO m) = case m realWorld# of (# _, r #) -> r
loop !sentinal !p acc
| p == sentinal = return acc
| otherwise = do x <- peek p
loop sentinal (p `plusPtr` (1)) (Step (x :> acc))
unsafeLast :: S.ByteString -> Word8
unsafeLast (S.PS x s l) =
accursedUnutterablePerformIO $ withForeignPtr x $ \p -> peekByteOff p (s+l1)
where
accursedUnutterablePerformIO (IO m) = case m realWorld# of (# _, r #) -> r
inlinePerformIO :: IO a -> a
inlinePerformIO (IO m) = case m realWorld# of (# _, r #) -> r
foldrChunks :: Monad m => (S.ByteString -> a -> a) -> a -> ByteString m r -> m a
foldrChunks step nil bs = dematerialize bs
(\_ -> return nil)
(liftM . step)
join
foldlChunks :: Monad m => (a -> S.ByteString -> a) -> a -> ByteString m r -> m (Of a r)
foldlChunks f z = go z
where go a _ | a `seq` False = undefined
go a (Empty r) = return (a :> r)
go a (Chunk c cs) = go (f a c) cs
go a (Go m) = m >>= go a
foldlChunksM :: Monad m => (a -> S.ByteString -> m a) -> m a -> ByteString m r -> m (Of a r)
foldlChunksM f z bs = z >>= \a -> go a bs
where
go !a str = case str of
Empty r -> return (a :> r)
Chunk c cs -> f a c >>= \aa -> go aa cs
Go m -> m >>= go a
foldrChunksM :: Monad m => (S.ByteString -> m a -> m a) -> m a -> ByteString m r -> m a
foldrChunksM step nil bs = dematerialize bs
(\_ -> nil)
step
join
unfoldrNE :: Int -> (a -> Either r (Word8, a)) -> a -> (S.ByteString, Either r a)
unfoldrNE i f x0
| i < 0 = (S.empty, Right x0)
| otherwise = unsafePerformIO $ S.createAndTrim' i $ \p -> go p x0 0
where
go !p !x !n
| n == i = return (0, n, Right x)
| otherwise = case f x of
Left r -> return (0, n, Left r)
Right (w,x') -> do poke p w
go (p `plusPtr` 1) x' (n+1)
unfoldMChunks :: Monad m => (s -> m (Maybe (S.ByteString, s))) -> s -> ByteString m ()
unfoldMChunks step = loop where
loop s = Go $ do
m <- step s
case m of
Nothing -> return (Empty ())
Just (bs,s') -> return $ Chunk bs (loop s')
unfoldrChunks :: Monad m => (s -> m (Either r (S.ByteString, s))) -> s -> ByteString m r
unfoldrChunks step = loop where
loop !s = Go $ do
m <- step s
case m of
Left r -> return (Empty r)
Right (bs,s') -> return $ Chunk bs (loop s')
reread :: Monad m => (s -> m (Maybe S.ByteString)) -> s -> ByteString m ()
reread step s = loop where
loop = Go $ do
m <- step s
case m of
Nothing -> return (Empty ())
Just a -> return (Chunk a loop)
copy
:: Monad m =>
ByteString m r -> ByteString (ByteString m) r
copy = loop where
loop str = case str of
Empty r -> Empty r
Go m -> Go (liftM loop (lift m))
Chunk bs rest -> Chunk bs (Go (Chunk bs (Empty (loop rest))))