streaming-bytestring 0.1.2.2 → 0.1.3.0
raw patch · 4 files changed
+139/−49 lines, 4 filesdep +exceptionsdep +resourcetdep +transformers-basedep ~streaming
Dependencies added: exceptions, resourcet, transformers-base
Dependency ranges changed: streaming
Files
- Data/ByteString/Streaming.hs +84/−40
- Data/ByteString/Streaming/Char8.hs +2/−2
- Data/ByteString/Streaming/Internal.hs +48/−5
- streaming-bytestring.cabal +5/−2
Data/ByteString/Streaming.hs view
@@ -169,6 +169,7 @@ -- * Etc. , zipWithStream -- zipWithStream :: Monad m => (forall x. a -> ByteString m x -> ByteString m x) -> [a] -> Stream (ByteString m) m r -> Stream (ByteString m) m r , distribute -- distribute :: ByteString (t m) a -> t (ByteString m) a + ) where import Prelude hiding@@ -207,6 +208,8 @@ import Foreign.Ptr import Data.Functor.Compose import Data.Functor.Sum+import Control.Monad.Trans.Resource+ -- | /O(n)/ Concatenate a stream of byte streams. concat :: Monad m => Stream (ByteString m) m r -> ByteString m r concat x = destroy x join Go Empty @@ -311,9 +314,9 @@ {-| /O(n)/ Convert a monadic byte stream into a single strict 'ByteString', retaining the return value of the original pair. This operation is- for use with 'mapsM'.+ for use with 'mapped'. -> mapsM R.toStrict :: Monad m => Stream (ByteString m) m r -> Stream (Of ByteString) m r +> mapped R.toStrict :: Monad m => Stream (ByteString m) m r -> Stream (Of ByteString) m r It is subject to all the objections one makes to Data.ByteString.Lazy 'toStrict'; all of these are devastating. @@ -361,9 +364,9 @@ followed by the memory of the succession of bytes. Because one preserves the return value, @toLazy@ is a suitable argument- for 'Streaming.mapsM'+ for 'Streaming.mapped' -> S.mapsM Q.toLazy :: Stream (ByteString m) m r -> Stream (Of L.ByteString) m r+> S.mapped Q.toLazy :: Stream (ByteString m) m r -> Stream (Of L.ByteString) m r >>> Q.toLazy "hello" "hello" :> ()@@ -410,7 +413,7 @@ -} denull :: Monad m => Stream (ByteString m) m r -> Stream (ByteString m) m r -denull = hoist (run . maps effects) . separate . mapsM nulls+denull = hoist (run . maps effects) . separate . mapped nulls {-#INLINE denull #-} {- | /O(1)/ Test whether a ByteString is empty, collecting its return value;@@ -420,7 +423,7 @@ False :> () >>> Q.null "" True :> ()->>> S.print $ mapsM R.null $ Q.lines "yours,\nMeredith"+>>> S.print $ mapped R.null $ Q.lines "yours,\nMeredith" False False @@ -443,11 +446,11 @@ There are many ways to remove null bytestrings from a @Stream (ByteString m) m r@ (besides using @denull@). If we pass next to ->>> mapsM nulls bs :: Stream (Sum (ByteString m) (ByteString m)) m r+>>> mapped nulls bs :: Stream (Sum (ByteString m) (ByteString m)) m r then can then apply @Streaming.separate@ to get ->>> separate (mapsM nulls bs) :: Stream (ByteString m) (Stream (ByteString m) m) r+>>> separate (mapped nulls bs) :: Stream (ByteString m) (Stream (ByteString m) m) r The inner monad is now made of the empty bytestrings; we act on this with @hoist@ , considering that @@ -458,7 +461,7 @@ we have ->>> hoist (Q.effects . Q.concat) . separate . mapsM Q.nulls+>>> hoist (Q.effects . Q.concat) . separate . mapped Q.nulls :: Monad n => Stream (Q.ByteString n) n b -> Stream (Q.ByteString n) n b @@ -483,7 +486,7 @@ >>> Q.length "one\ntwo\three\nfour\nfive\n" 23 :> ()->>> S.print $ S.take 3 $ mapsM Q.length $ Q.lines "one\ntwo\three\nfour\nfive\n" +>>> S.print $ S.take 3 $ mapped Q.length $ Q.lines "one\ntwo\three\nfour\nfive\n" 3 8 4@@ -1104,16 +1107,19 @@ where -- comb :: [P.ByteString] -> [P.ByteString] -> ByteString -> [ByteString] -- comb acc (s:[]) (Empty r) = Step (revChunks (s:acc) (Return r))- comb acc [s] (Empty r) = Step $ L.foldl' (flip Chunk) - (Empty (Return r)) - (s:acc) - comb acc [s] (Chunk c cs) = comb (s:acc) (S.splitWith p c) cs- comb acc b (Go m) = Effect (liftM (comb acc b) m)- comb acc (s:ss) cs = Step $ L.foldl' (flip Chunk) - (Empty (comb [] ss cs)) - (s:acc)- --- comb acc (s:ss) cs = Step (revChunks (s:acc) (comb [] ss cs))+ comb acc [s] (Empty r) = Step $ L.foldl' (flip Chunk) + (Empty (Return r)) + (s:acc) + comb acc [s] (Chunk c cs) = comb (s:acc) (S.splitWith p c) cs+ comb acc b (Go m) = Effect (liftM (comb acc b) m)+ comb acc (s:ss) cs = Step $ L.foldl' (flip Chunk) + (Empty (comb [] ss cs)) + (s:acc)+ comb acc [] (Empty r) = Step $ L.foldl' (flip Chunk) + (Empty (Return r)) + acc + comb acc [] (Chunk c cs) = comb acc (S.splitWith p c) cs + -- comb acc (s:ss) cs = Step (revChunks (s:acc) (comb [] ss cs)) {-# INLINABLE splitWith #-} @@ -1469,29 +1475,67 @@ hGetNonBlocking = hGetNonBlockingN defaultChunkSize {-#INLINE hGetNonBlocking #-} --- | Read an entire file into a chunked 'ByteString IO ()'.--- The Handle will be held open until EOF is encountered.----readFile :: MonadIO m => FilePath -> ByteString m ()-readFile f = Go $ liftM hGetContents (liftIO (openBinaryFile f ReadMode))-{-#INLINE readFile #-}+{-| Write a 'ByteString' to a file. Use 'Control.Monad.Trans.ResourceT.runResourceT'+ to ensure that the handle is closed. --- | Write a 'ByteString' to a file.----writeFile :: FilePath -> ByteString IO r -> IO r-writeFile f txt = bracket- (openBinaryFile f WriteMode)- hClose- (\hdl -> hPut hdl txt)+>>> :set -XOverloadedStrings+>>> runResourceT $ Q.writeFile "hello.txt" "Hello world.\nGoodbye world.\n" +>>> :! cat "hello.txt"+Hello world.+Goodbye world.+>>> runResourceT $ Q.writeFile "hello2.txt" $ Q.readFile "hello.txt"+>>> :! cat hello2.txt +Hello world.+Goodbye world.++ -}+writeFile :: MonadResource m => FilePath -> ByteString m r -> m r+writeFile f str = do+ (key, handle) <- allocate (openBinaryFile f WriteMode) hClose+ r <- hPut handle str+ release key+ return r {-# INLINE writeFile #-} --- | Append a 'ByteString' to a file.----appendFile :: FilePath -> ByteString IO r -> IO r-appendFile f txt = bracket- (openBinaryFile f AppendMode)- hClose- (\hdl -> hPut hdl txt)+{-| Read an entire file into a chunked 'ByteString IO ()'.+ The Handle will be held open until EOF is encountered.+ The block governed by 'Control.Monad.Trans.Resource.runResourceT'+ will end with the closing of any handles opened.++>>> :! cat hello.txt+Hello world.+Goodbye world. +>>> runResourceT $ Q.stdout $ Q.readFile "hello.txt"+Hello world.+Goodbye world. + -}++readFile :: MonadResource m => FilePath -> ByteString m ()+readFile f = bracketByteString (openBinaryFile f ReadMode) hClose hGetContents+{-#INLINE readFile #-}++++{-| Append a 'ByteString' to a file. Use 'Control.Monad.Trans.ResourceT.runResourceT'+ to ensure that the handle is closed. ++>>> runResourceT $ Q.writeFile "hello.txt" "Hello world.\nGoodbye world.\n"+>>> runResourceT $ Q.stdout $ Q.readFile "hello.txt"+Hello world.+Goodbye world.+>>> runResourceT $ Q.appendFile "hello.txt" "sincerely yours,\nArthur\n"+>>> runResourceT $ Q.stdout $ Q.readFile "hello.txt"+Hello world.+Goodbye world.+sincerely yours,+Arthur+ -}+appendFile :: MonadResource m => FilePath -> ByteString m r -> m r+appendFile f str = do+ (key, handle) <- allocate (openBinaryFile f AppendMode) hClose+ r <- hPut handle str+ release key+ return r {-# INLINE appendFile #-} -- | getContents. Equivalent to hGetContents stdin. Will read /lazily/
Data/ByteString/Streaming/Char8.hs view
@@ -216,7 +216,7 @@ -- | /O(n)/ Convert a stream of separate characters into a packed byte stream. pack :: Monad m => Stream (Of Char) m r -> ByteString m r pack = fromChunks - . mapsM (liftM (\(str :> r) -> Char8.pack str :> r) . S.toList) + . mapped (liftM (\(str :> r) -> Char8.pack str :> r) . S.toList) . chunksOf 32 {-# INLINABLE pack #-} @@ -502,7 +502,7 @@ -- the genuinely streaming 'words'. A function that returns individual -- strict bytestrings would concatenate even infinitely -- long words like @cycle "y"@ in memory. It is best for the user who--- has reflected on her materials to write `mapsM toStrict . words` or the like,+-- has reflected on her materials to write `mapped toStrict . words` or the like, -- if strict bytestrings are needed. words :: Monad m => ByteString m r -> Stream (ByteString m) m r words = filtered . R.splitWith B.isSpaceWord8
Data/ByteString/Streaming/Internal.hs view
@@ -1,6 +1,7 @@-{-# LANGUAGE CPP, BangPatterns #-}-{-#LANGUAGE RankNTypes, GADTs #-}+{-# LANGUAGE CPP, BangPatterns, RankNTypes, GADTs #-} {-# LANGUAGE UnliftedFFITypes, MagicHash, UnboxedTuples #-}+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, UndecidableInstances #-} +-- for resourcet etc. module Data.ByteString.Streaming.Internal ( ByteString (..) , consChunk -- :: S.ByteString -> ByteString m r -> ByteString m r@@ -26,6 +27,9 @@ , reread , inlinePerformIO , unsafeLast+ + -- * ResourceT help+ , bracketByteString ) where import Prelude hiding@@ -60,6 +64,9 @@ import GHC.Base (realWorld#,unsafeChr) import GHC.IO (IO(IO)) +import Control.Monad.Base+import Control.Monad.Trans.Resource+import Control.Monad.Catch (MonadCatch (..)) -- | A space-efficient representation of a succession of 'Word8' vectors, supporting many -- efficient operations. --@@ -133,9 +140,45 @@ {-# INLINE mempty #-} mappend = liftM2 mappend {-# INLINE mappend #-}- --- data Word8_ r = Word8_ {-#UNPACK#-} !Word8 r --- This might be preferable to (Of Word8 r), but the present approach is simpler.++instance (MonadBase b m) => MonadBase b (ByteString m) where+ liftBase = mwrap . fmap return . liftBase+ {-#INLINE liftBase #-}++instance (MonadThrow m) => MonadThrow (ByteString m) where+ throwM = lift . throwM + {-#INLINE 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)) )+ {-#INLINABLE catch #-}+ +instance (MonadResource m) => MonadResource (ByteString m) where+ liftResourceT = lift . liftResourceT+ {-#INLINE 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)+{-#INLINABLE bracketByteString #-}+ data SPEC = SPEC | SPEC2 {-# ANN type SPEC ForceSpecConstr #-}
streaming-bytestring.cabal view
@@ -1,5 +1,5 @@ name: streaming-bytestring-version: 0.1.2.2+version: 0.1.3.0 synopsis: effectful byte steams, or: bytestring io done right. description: This is an implementation of effectful, memory-constrained @@ -189,7 +189,10 @@ , mtl >=2.1 && <2.3 , mmorph >=1.0 && <1.2 , transformers >=0.3 && <0.5- , streaming > 0.1.2.0 && < 0.1.2.4+ , transformers-base+ , streaming >= 0.1.3.0 && <= 0.1.3.5+ , resourcet+ , exceptions if impl(ghc < 7.8) build-depends: bytestring < 0.10.4.0