packages feed

pcapng-0.1.0.0: src/Data/WordString32/Conduit.hs

module Data.WordString32.Conduit(
    sourceFile
  , sinkFile
  , atLeast
  ) where

import           Control.Monad.Trans.Resource (MonadResource (..))
import qualified Data.ByteString              as BS
import           Data.Conduit                 (ConduitT, await, yield, (.|))
import qualified Data.Conduit.Binary          as C
import qualified Data.Conduit.List            as C
import qualified Data.WordString32            as WS

import           Debug.Trace                  (trace)

sourceFile :: (MonadResource m
              ,MonadFail     m)
           =>  FilePath
           ->  ConduitT () WS.WordString m ()
sourceFile filepath = C.sourceFile filepath .| go
  where
    go = do
      maybeBs <- trace "sourceFile chunk" $ await
      case maybeBs of
        Nothing -> return ()
        Just bs | BS.length bs `mod` 4 /= 0 -> do
          -- OMG, why do we need to do this (hspec error)
          let msg = "Incorrect PCAP file - not aligned to 4 byte boundary: " <> show (BS.length bs)
          trace msg $
            yield $ WS.fromBS $ BS.take ((BS.length bs `div` 4)*4) bs
          return () -- fail after partial read
        Just bs -> yield $ WS.fromBS bs

sinkFile :: MonadResource m => FilePath -> ConduitT WS.WordString () m ()
sinkFile filepath = C.map WS.toBS .| C.sinkFile filepath

-- | Make sure that we give chunks of at least N words
--
--   This conduit has a hidden state, so cannot be easily aborted.
-- NOTE: example of how conduit could get better by feedback upstream (needed input size)
-- WARNING: may lead to suboptimal performance if make misaligned reads
atLeast  :: MonadFail                            m
         => Int
         -> ConduitT WS.WordString WS.WordString m ()
atLeast n = go WS.empty
  where
    go rest = do
      maybeChunk <- await
      case maybeChunk of
        Nothing | WS.null rest -> return ()
        Nothing                -> fail
                                $ mconcat ["Not enough bytes to satisfy atLeast: ",
                                           show $ WS.length rest, " wanting: ", show n]
        Just chunk | WS.length rest + WS.length chunk >= n ->
          trace "long chunk" $
            yield $ rest <> chunk
        Just shortChunk -> trace "short chunk" $ go $ rest <> shortChunk