packages feed

binary-io 0.0.1 → 0.1.0

raw patch · 5 files changed

+44/−68 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.Binary.IO: readWith :: (CanGet r, Binary a) => r -> (a -> IO b) -> IO b
- Data.Binary.IO: runGet :: CanGet r => r -> Get a -> (a -> IO b) -> IO b
+ Data.Binary.IO: runGet :: CanGet r => r -> Get a -> IO a

Files

ChangeLog.md view
@@ -1,5 +1,9 @@ # binary-io -## 1.0.0+## 0.1.0++* Remove continuation parameter to 'runGet' and 'readWith'++## 0.0.1  * Inception
binary-io.cabal view
@@ -5,7 +5,7 @@   binary-io  version:-  0.0.1+  0.1.0  category:   Data, Parsing, IO
lib/Data/Binary/IO.hs view
@@ -19,7 +19,6 @@     -- * Classes   , CanGet (..)   , read-  , readWith    , CanPut (..)   , write@@ -29,15 +28,15 @@ import Prelude hiding (read)  import qualified Control.Exception as Exception-import qualified Control.Concurrent.MVar as MVar--import qualified Data.ByteString.Lazy as ByteString-import qualified Data.ByteString as ByteString.Strict+import           Control.Monad (join)+import           Data.Bifunctor (bimap)+import qualified Data.Binary as Binary import qualified Data.Binary.Get as Binary.Get import qualified Data.Binary.Put as Binary.Put-import qualified Data.Binary as Binary--import System.IO (Handle, hSetBinaryMode)+import qualified Data.ByteString as ByteString.Strict+import qualified Data.ByteString.Lazy as ByteString+import           Data.IORef (IORef, atomicModifyIORef, newIORef)+import           System.IO (Handle, hSetBinaryMode)  -- * Reader @@ -69,22 +68,22 @@  newtype StationaryReader = StationaryReader ByteString.ByteString -runStationaryReader :: StationaryReader -> Binary.Get.Get a -> IO (StationaryReader, a)-runStationaryReader (StationaryReader stream) getter =  do-  -- Evaluate the result of 'runGetOrFail' to WHNF. This should be enough because it means that-  -- the parser has decided between 'Left' and 'Right'.-  result <- Exception.evaluate (Binary.Get.runGetOrFail getter stream)-  case result of-    Left (remainingBody, offset, errorMessage) ->-      Exception.throw ReaderGetError+runStationaryReader+  :: StationaryReader+  -> Binary.Get.Get a+  -> Either ReaderError (StationaryReader, a)+runStationaryReader (StationaryReader stream) getter =+  bimap withError withSuccess (Binary.Get.runGetOrFail getter stream)+  where+    withError (remainingBody, offset, errorMessage) =+      ReaderGetError         { readerErrorRemaining = remainingBody         , readerErrorOffset = offset         , readerErrorInput = stream         , readerErrorMessage = errorMessage         } -    Right (tailStream, _, value) ->-      pure (StationaryReader tailStream, value)+    withSuccess (tailStream, _, value) = (StationaryReader tailStream, value)  newStationaryReader :: Handle -> IO StationaryReader newStationaryReader handle = do@@ -92,13 +91,17 @@   StationaryReader <$> ByteString.hGetContents handle  -- | @since 0.0.1-newtype Reader = Reader (MVar.MVar StationaryReader)+newtype Reader = Reader (IORef StationaryReader) -runReader :: Reader -> Binary.Get a -> (a -> IO b) -> IO b-runReader (Reader readerVar) getter continue =-  MVar.modifyMVar readerVar $ \posReader -> do-    toReturn <- runStationaryReader posReader getter-    traverse continue toReturn+runReader :: Reader -> Binary.Get a -> IO a+runReader (Reader readerVar) getter =+  -- We use 'atomicModifyIORef' which does not force the 'StationaryReader' to WHNF. Forcing the+  -- 'StationaryReader' might block indefinitely because it will try to read more from the+  -- underlying 'Handle'.+  join $ atomicModifyIORef readerVar $ \posReader ->+    case runStationaryReader posReader getter of+      Left error   -> (posReader, Exception.throwIO error)+      Right result -> pure <$> result  -- | Create a new reader. --@@ -106,8 +109,7 @@ -- -- The internal position of the 'Reader' is not advanced when it throws an exception during reading. -- This has the consequence that if you're trying to read with the same faulty 'Binary.Get'--- operation multiple times, you will always receive an exception. The same is true for follow-up--- actions when using 'readWith'.+-- operation multiple times, you will always receive an exception. -- -- Other threads reading from the 'Handle' will interfere with read operations of the 'Reader'. -- However, the 'Reader' itself is thread-safe and can be utilized concurrently.@@ -122,7 +124,7 @@   -> IO Reader newReader handle = do   posReader <- newStationaryReader handle-  Reader <$> MVar.newMVar posReader+  Reader <$> newIORef posReader  -- * Writer @@ -173,8 +175,7 @@   runGet     :: r -- ^ Reader / source     -> Binary.Get a -- ^ Operation to execute-    -> (a -> IO b) -- ^ What to do with @a@-    -> IO b+    -> IO a  instance CanGet Reader where   runGet = runReader@@ -208,23 +209,6 @@   => r -- ^ Read source   -> IO a read reader =-  runGet reader Binary.get pure---- | Read something from @r@ and perform an 'IO' action with it.------ If the given action throws an exception, the read is not considered successful and will not--- advance the underlying read source.------ Keep in mind, long running actions on @a@ will block other threads when they try to read the from--- the same source @r@.------ @since 0.0.1-readWith-  :: (CanGet r, Binary.Binary a)-  => r -- ^ Read source-  -> (a -> IO b) -- ^ What to do with @a@-  -> IO b-readWith reader =   runGet reader Binary.get  -- | Write something to @w@.
test/Data/Binary/IOSpec.hs view
@@ -4,21 +4,21 @@  import Prelude hiding (read) -import Control.Monad.IO.Class (MonadIO (liftIO))+import Control.Exception (Exception) import Control.Monad (join)-import Control.Exception (Exception, throw)+import Control.Monad.IO.Class (MonadIO (liftIO)) -import Data.Typeable (typeOf)-import Data.List (isInfixOf)-import Data.Binary.IO-import Data.Binary (Binary (..)) import Data.Bifoldable (bitraverse_)+import Data.Binary (Binary (..))+import Data.Binary.IO+import Data.List (isInfixOf)+import Data.Typeable (typeOf)  import qualified Test.Hspec as Hspec -import           System.Process (createPipe) import qualified System.IO as IO-import           System.IO.Error (isIllegalOperation, ioeGetErrorString)+import           System.IO.Error (ioeGetErrorString, isIllegalOperation)+import           System.Process (createPipe)  -- | Create a pipe with no buffering on read and write side. createUnbufferedPipe :: IO (IO.Handle, IO.Handle)@@ -102,18 +102,6 @@        write handleWrite "Hello World"       Hspec.shouldThrow (read reader :: IO BadGet) (\ReaderGetError{} -> True)-      "Hello World" <- read reader--      pure ()--    -- Failing continuations should not advance the stream position.-    Hspec.it "preserves the stream position when continuation fails" $ \(handleRead, handleWrite) -> do-      reader <- liftIO (newReader handleRead)--      write handleWrite "Hello World"-      Hspec.shouldThrow-        (readWith reader (\() -> throw ExampleException))-        (\ExampleException -> True)       "Hello World" <- read reader        pure ()
test/Main.hs view