-- from "base"
import Control.Exception (bracket)
import qualified System.IO as IO

-- from "bytestring"
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy as L

-- from "enumerator"
import Data.Enumerator (($$))
import qualified Data.Enumerator as E
import qualified Data.Enumerator.Binary as EB


chunkSize, bufferSize :: Int
chunkSize  = 512
bufferSize = chunkSize * 32

searchForPattern :: Monad m => B.ByteString -> E.Iteratee B.ByteString m (Maybe Int)
searchForPattern pat = go 0
    where
      patL    = L.fromChunks [pat]
      patLen  = B.length pat
      restLen = chunkSize - patLen
      go i = do str <- EB.take (fromIntegral patLen)
                case (L.length str < fromIntegral patLen, str == patL) of
                  (True, _) -> return Nothing
                  (_, True) -> return (Just i)
                  _         -> do EB.drop (fromIntegral restLen)
                                  go $! i+1

main :: IO ()
main =
  bracket (IO.openBinaryFile "blocks" IO.ReadMode) IO.hClose $ \fromHandle -> do
    let pat = B8.pack "PART"
    result <- E.run $ EB.enumHandle (fromIntegral bufferSize) fromHandle $$
                      searchForPattern pat
    case result of
      Left exception -> print exception
      Right Nothing  -> putStrLn "Not found."
      Right (Just i) -> putStrLn $ "Found at block " ++ show i ++ "."
