packages feed

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 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