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 +5/−1
- binary-io.cabal +1/−1
- lib/Data/Binary/IO.hs +30/−46
- test/Data/Binary/IOSpec.hs +8/−20
- test/Main.hs +0/−0
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