streaming-binary (empty) → 0.1.0.0
raw patch · 8 files changed
+184/−0 lines, 8 filesdep +basedep +binarydep +bytestringsetup-changed
Dependencies added: base, binary, bytestring, hspec, streaming, streaming-binary, streaming-bytestring
Files
- LICENSE +30/−0
- README.md +6/−0
- Setup.hs +2/−0
- src/Streaming/Binary.hs +57/−0
- streaming-binary.cabal +45/−0
- test/Main.hs +7/−0
- test/Spec.hs +1/−0
- test/Streaming/BinarySpec.hs +36/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Mathieu Boespflug (c) 2017++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Mathieu Boespflug nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,6 @@+# streaming-binary++This library offers incremental serialization and deserialization of+Haskell values using the `binary` package. Under the hood, we wrap the+incremental decoders from `binary`'s Incremental API as first-class+streams as defined in `streaming`.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Streaming/Binary.hs view
@@ -0,0 +1,57 @@+-- | This module implements a method to ingest a sequence of "Data.Binary"+-- encoded records using bounded memory. Minimal example:+--+-- > {-# LANGUAGE TypeApplications #-}+-- >+-- > import Data.Function ((&))+-- > import qualified Data.ByteString.Streaming as Q+-- > import Streaming+-- > import Streaming.Binary+-- > import qualified Streaming.Prelude as S+-- >+-- > -- Interpret all bytes on stdin as a sequence of integers.+-- > -- Print them on-the-fly on stdout.+-- > main = Q.getContents & decoded @Int & S.print++{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}++module Streaming.Binary (decoded) where++import qualified Data.Binary.Get as Binary+import Data.Binary (Binary, get)+import qualified Data.ByteString.Streaming as Q+import Data.ByteString.Streaming (ByteString)+import Data.Int (Int64)+import Streaming+import qualified Streaming.Prelude as S++_decode+ :: (Binary a, Monad m)+ => ByteString m r+ -> m (ByteString m r, Int64, Either String a)+_decode = undefined -- TODO implement this.++decoded+ :: (Binary a, Monad m)+ => ByteString m r+ -> Stream (Of a) m (ByteString m r, Int64, Either String r)+decoded = go 0 decoder0+ where+ decoder0 = Binary.runGetIncremental get+ go !total (Binary.Fail leftover nconsumed err) p = do+ return (Q.chunk leftover >> p, total + nconsumed, Left err)+ go !total (Binary.Done "" nconsumed x) p = do+ S.yield x+ lift (Q.nextChunk p) >>= \case+ Left res -> return (return res, total + nconsumed, Right res)+ Right (bs, p') -> do+ go (total + nconsumed) decoder0 (Q.chunk bs >> p')+ go !total (Binary.Done leftover nconsumed x) p = do+ S.yield x+ go (total + nconsumed) decoder0 (Q.chunk leftover >> p)+ go !total (Binary.Partial k) p = do+ lift (Q.nextChunk p) >>= \case+ Left res -> go total (k Nothing) (return res)+ Right (bs, p') -> go total (k (Just bs)) p'
+ streaming-binary.cabal view
@@ -0,0 +1,45 @@+name: streaming-binary+version: 0.1.0.0+synopsis: Streaming interface to binary.+homepage: https://github.com/mboes/streaming-binary#readme+license: BSD3+license-file: LICENSE+author: Mathieu Boespflug+maintainer: m@tweag.io+copyright: (c) 2017 Mathieu Boespflug+category: Streaming, Parsing+build-type: Simple+extra-source-files: README.md+cabal-version: >=1.10++source-repository head+ type: git+ location: https://github.com/mboes/streaming-binary++library+ hs-source-dirs: src+ exposed-modules: Streaming.Binary+ build-depends:+ base >= 4.7 && < 5,+ binary >= 0.8,+ streaming >= 0.1.4,+ streaming-bytestring >= 0.1.4+ default-language: Haskell2010++test-suite tests+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Main.hs+ other-modules:+ Spec+ Streaming.BinarySpec+ build-depends:+ base,+ binary,+ bytestring >= 0.10,+ hspec >= 2.4,+ streaming,+ streaming-binary,+ streaming-bytestring+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ default-language: Haskell2010
+ test/Main.hs view
@@ -0,0 +1,7 @@+module Main where++import qualified Spec+import Test.Hspec++main :: IO ()+main = hspec Spec.spec
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Spec #-}
+ test/Streaming/BinarySpec.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE TypeApplications #-}++module Streaming.BinarySpec where++import Control.Monad (replicateM_, void)+import Data.Binary (put)+import Data.Binary.Put (runPut)+import Data.Function ((&))+import qualified Data.ByteString.Streaming as Q+import Streaming.Binary+import qualified Streaming.Prelude as S+import Test.Hspec++spec :: Spec+spec = do+ let input n = Q.fromLazy $ runPut $ replicateM_ n $ put (42 :: Int)+ describe "decoded" $ do+ it "succeeds on empty inputs" $ do+ output <- void (decoded @Int (input 0)) & S.toList_+ output `shouldBe` []+ it "decodes single integers" $ do+ output <- void (decoded @Int (input 1)) & S.toList_+ output `shouldBe` [42]+ it "decodes multiple integers" $ do+ output <- void (decoded @Int (input 10)) & S.toList_+ output `shouldBe` (replicate 10 42)+ it "decodes multiple integers even when the input is incomplete" $ do+ n <- fromIntegral <$> Q.length_ (input 10)+ let input' = Q.take (n - 1) (input 10)+ output <- void (decoded @Int input') & S.toList_+ output `shouldBe` (replicate 9 42)+ it "leaves the right amount of leftover on incomplete input" $ do+ n <- Q.length_ (input 10)+ let input' = Q.take (fromIntegral (n - 1)) (input 10)+ (leftover, _, _) <- S.effects (decoded @Int input')+ Q.length_ leftover `shouldReturn` (n `div` 10) - 1