1 つのネットワーク プロトコルについて、さまざまな種類のチャンクをSource m ByteString
. 入力を行に分割するコンビネータがありlines
ますが、読み取り行と固定数のバイトを組み合わせることができる必要があります。
私の現在のアプローチは、ヘルパー関数を作成することです。
| | 入力で指定された関数を折りたたみます。関数が戻るまで繰り返し、
Left
その結果をリストに蓄積します。関数が を返したらRight
、累積された結果 (最後の結果を含む) を連結して返し、 を使用して残ったものを格納しleftover
ます。Nothing
入力が利用できない場合に戻ります。
chunk :: (Monad m, Monoid a)
=> (s -> i -> Either (a, s) (a, i))
-> s
-> Consumer i m (Maybe a)
chunk f = loop []
where
loop xs s = await >>= maybe (emit xs) (go xs s)
go xs s i = case f s i of
Left (x, s') -> loop (x : xs) s'
Right (x, l) -> leftover l >> emit (x : xs)
emit [] = return Nothing
emit xs = return (Just . mconcat . L.reverse $ xs)
-- Note: We could use `mappend` to combine the chunks directly. But this would
-- often get us O(n^2) complexity (like for `ByteString`s) so we keep a list of
-- the chunks and then use `mconcat`, which can be optimized by the `Monoid`.
この関数を使用して、特定のコンシューマーを作成します。
bytes :: (Monad m) => Int -> Consumer ByteString m (Maybe ByteString)
bytes = chunk f
where
f n bs | n' > 0 = Left (bs, n')
| otherwise = Right $ BS.splitAt n bs
where n' = n - BS.length bs
line :: (Monad m) => Consumer ByteString m (Maybe ByteString)
line = chunk f ()
where
f _ bs = maybe (Left (bs, ()))
(\i -> Right $ BS.splitAt (i + 1) bs)
(BS.findIndex (== '\n') bs)
より良い方法はありますか?この問題はどこかですでに解決されているはずです。