エンコードされた大きな(<bloblength><blob>)*
バイナリ ファイルのデシリアライザーを作成しているときに、さまざまな Haskell の Produce-Transform-Consume ライブラリに行き詰まりました。これまでのところ、4 つのストリーミング ライブラリを認識しています。
- Data.Conduit : 広く使用されており、非常に慎重なリソース管理が行われています
- Pipes : 同様
conduit
( Haskell Cast #6は と の違いをうまく明らかにしていconduit
ますpipes
) - Data.Binary.Get : getWord32be などの便利な関数を提供しますが、ストリーミングの例は扱いにくいです
- System.IO.Streams : 一番使いやすいようです
Word32
これは、ストリーミングをしようとしたときに問題が発生する場所の簡略化された例ですconduit
。もう少し現実的な例では、最初Word32
に blob の長さを決定する a を読み取り、次にその長さの lazyByteString
を生成します (その後、さらにデシリアライズされます)。しかし、ここでは、バイナリ ファイルからストリーミング形式で Word32 を抽出しようとしています。
module Main where
-- build-depends: bytestring, conduit, conduit-extra, resourcet, binary
import Control.Monad.Trans.Resource (MonadResource, runResourceT)
import qualified Data.Binary.Get as G
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString.Lazy as BL
import Data.Conduit
import qualified Data.Conduit.Binary as CB
import qualified Data.Conduit.List as CL
import Data.Word (Word32)
import System.Environment (getArgs)
-- gets a Word32 from a ByteString.
getWord32 :: C.ByteString -> Word32
getWord32 bs = do
G.runGet G.getWord32be $ BL.fromStrict bs
-- should read BytesString and return Word32
transform :: (Monad m, MonadResource m) => Conduit BS.ByteString m Word32
transform = do
mbs <- await
case mbs of
Just bs -> do
case C.null bs of
False -> do
yield $ getWord32 bs
leftover $ BS.drop 4 bs
transform
True -> return ()
Nothing -> return ()
main :: IO ()
main = do
filename <- fmap (!!0) getArgs -- should check length getArgs
result <- runResourceT $ (CB.sourceFile filename) $$ transform =$ CL.consume
print $ length result -- is always 8188 for files larger than 32752 bytes
プログラムの出力は、読み取られた Word32 の数だけです。最初のチャンク (約 32KiB) を読み取った後、ストリームが終了することがわかります。何らかの理由mbs
でが neverであるため、チャンクが消費されたときにストリームを停止するものをNothing
確認する必要があります。null bs
明らかに、私のコンジットtransform
は故障しています。ソリューションへの 2 つのルートが表示されます。
- はの
await
2 番目のチャンクに行きたくないByteStream
ので、次のチャンクをプルする別の関数はありますか? 私が見た例 (例: Conduit 101 ) では、これはどのように行われたかではありません - これは、セットアップの方法が間違っているだけ
transform
です。
これはどのように適切に行われますか?これは正しい方法ですか?(パフォーマンスは重要です。)
更新:これを使用してそれを行う悪い方法は次のSystems.IO.Streams
とおりです。
module Main where
import Data.Word (Word32)
import System.Environment (getArgs)
import System.IO (IOMode (ReadMode), openFile)
import qualified System.IO.Streams as S
import System.IO.Streams.Binary (binaryInputStream)
import System.IO.Streams.List (outputToList)
main :: IO ()
main = do
filename : _ <- getArgs
h <- openFile filename ReadMode
s <- S.handleToInputStream h
i <- binaryInputStream s :: IO (S.InputStream Word32)
r <- outputToList $ S.connect i
print $ last r
「悪い」とは、時間と空間の要求が非常に高く、デコード例外を処理しないことを意味します。