binary-io 0.6.1 → 0.6.2
raw patch · 4 files changed
+43/−29 lines, 4 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Data.Binary.IO.Lifted: newWriterWith :: Applicative m => (ByteString -> m ()) -> Writer m
+ Data.Binary.IO.Lifted: newWriterWith :: Functor m => (ByteString -> m ()) -> Writer m
Files
- ChangeLog.md +5/−0
- binary-io.cabal +1/−1
- lib/Data/Binary/IO/Lifted.hs +17/−27
- test/Data/Binary/IOSpec.hs +20/−1
ChangeLog.md view
@@ -1,5 +1,10 @@ # binary-io +## 0.6.2++* Revert changes from 0.6.0 in order to preserve write atomicity+* Implement StationaryReader which powers Reader without concurrency primitives+ ## 0.6.1 * Remove eta-reductions that cause GHC 9 builds to fail
binary-io.cabal view
@@ -5,7 +5,7 @@ binary-io version:- 0.6.1+ 0.6.2 category: Data, Parsing, IO
lib/Data/Binary/IO/Lifted.hs view
@@ -45,7 +45,6 @@ import Prelude hiding (read) -import Control.Arrow ((&&&)) import qualified Control.Concurrent.Classy as Concurrent import Control.Monad (join, unless) import qualified Control.Monad.Catch as Catch@@ -58,8 +57,7 @@ import qualified Data.Binary.Put as Put import Data.ByteString (ByteString) import qualified Data.ByteString as ByteString-import Data.ByteString.Lazy (toChunks, toStrict)-import Data.Foldable (traverse_)+import Data.ByteString.Lazy (toStrict) import Data.IORef (atomicModifyIORef', mkWeakIORef, newIORef) import qualified Deque.Strict as Deque import System.IO (Handle, hSetBinaryMode)@@ -103,43 +101,36 @@ newStationaryReaderWith :: forall m- . Concurrent.MonadConc m+ . Catch.MonadMask m => m ByteString -> m (StationaryReader m) newStationaryReaderWith getChunk = do- inputRef <- Concurrent.newIORef ByteString.empty- let- make = StationaryReader $ \get -> do- input <- lift $ Concurrent.readIORef inputRef- loop $ Get.pushChunk (Get.runGetIncremental get) input+ make initialInput = StationaryReader $ \get ->+ loop initialInput $ Get.pushChunk (Get.runGetIncremental get) initialInput - loop :: Get.Decoder a -> ExceptT ReaderError m (StationaryReader m, a)- loop = \case- Get.Fail remainingBody offset errorMessage -> do- input <- lift $ Concurrent.readIORef inputRef+ loop :: ByteString -> Get.Decoder a -> ExceptT ReaderError m (StationaryReader m, a)+ loop inputForError = \case+ Get.Fail remainingBody offset errorMessage -> except $ Left ReaderGetError { readerErrorRemaining = remainingBody , readerErrorOffset = offset- , readerErrorInput = input+ , readerErrorInput = inputForError , readerErrorMessage = errorMessage } Get.Done remainingBody _ value -> Catch.mask_ $ do- lift $ Concurrent.writeIORef inputRef remainingBody- pure (make, value)+ pure (make remainingBody, value) Get.Partial continue -> do- chunk <- lift $ Catch.mask $ \restore -> do- chunk <- restore getChunk+ chunk <- lift getChunk+ loop (inputForError <> chunk) $ continue $ if ByteString.null chunk then- pure Nothing+ Nothing else- Concurrent.atomicModifyIORef' inputRef $ (<> chunk) &&& const (Just chunk)-- loop $ continue chunk+ Just chunk - pure make+ pure (make ByteString.empty) -- | @since 0.4.0 newtype Reader m = Reader@@ -242,14 +233,13 @@ -- -- @since 0.4.0 newWriterWith- :: Applicative m+ :: Functor m => (ByteString -> m ()) -- ^ Chunk writer -> Writer m newWriterWith write = Writer $ \put -> do let (result, body) = Put.runPutM put- result <$- -- Instead of allocating a single big- traverse_ write (toChunks body)+ -- We allocate one big ByteString chunk to preserve write atomicity.+ result <$ write (toStrict body) -- | Create a writer. --
test/Data/Binary/IOSpec.hs view
@@ -10,9 +10,12 @@ import Data.Bifoldable (bitraverse_) import Data.Binary (Binary (..), encode) import Data.Binary.IO+import Data.Binary.Put (putByteString) import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Char8 as ByteString.Char8 import qualified Data.ByteString.Lazy as ByteString.Lazy import Data.Foldable (for_)+import Data.IORef (atomicModifyIORef', newIORef, readIORef) import Data.List (isInfixOf) import Data.Typeable (typeOf) import qualified System.IO as IO@@ -80,7 +83,7 @@ withPipe = Hspec.before createUnbufferedPipe spec :: Hspec.Spec-spec = do+spec = Hspec.parallel $ do Hspec.describe "Reader" $ do let testReads value =@@ -181,6 +184,22 @@ closeHandle handleWrite Hspec.shouldThrow (write writer "Hello World") isIllegalOperation++ Hspec.it "preserves atomicity" $ do+ chunksRef <- newIORef []++ let+ writer = newWriterWith $ \chunk -> atomicModifyIORef' chunksRef $ \chunks ->+ (chunks ++ [chunk], ())++ runPut writer $ putByteString $ ByteString.Char8.pack "Hello"+ runPut writer $ putByteString $ ByteString.Char8.pack "World"+ runPut writer $ do+ putByteString $ ByteString.Char8.pack "Hello"+ putByteString $ ByteString.Char8.pack "World"++ chunks <- readIORef chunksRef+ chunks `Hspec.shouldBe` map ByteString.Char8.pack ["Hello", "World", "HelloWorld"] Hspec.describe "Pipe" $ do Hspec.it "is connected" $ do