module Data.Attoparsec.Split (split) where
import Data.Attoparsec (Parser, Result, IResult(..), parse)
import Data.ByteString.Lazy.Internal (ByteString(..), chunk)
import qualified Data.ByteString.Lazy as Lazy
import qualified Data.ByteString as B
import qualified Data.ByteString as Strict
import Data.Maybe (fromMaybe, isJust)
data SplitResult =
Match !B.ByteString
!ByteString
!Bool
| SplitChunk !B.ByteString
deriving Show
isChunk :: SplitResult -> Bool
isChunk (SplitChunk _) = True
isChunk _ = False
asChunk :: SplitResult -> B.ByteString
asChunk (SplitChunk c) = c
asChunk _ = B.empty
split :: Parser Strict.ByteString -> Lazy.ByteString -> [Lazy.ByteString]
split bdry = firstSplit . splitOne True bdry
where
firstSplit [] = []
firstSplit result@(Match _ _ _ : _) = continue result
firstSplit result = nextSplit B.empty result
nextSplit pfx result =
chunk pfx (foldr (chunk . asChunk) Empty result) :
continue (dropWhile isChunk result)
continue (Match pfx xs bump : _) = nextSplit pfx (splitOne bump bdry xs)
continue _ = []
splitOne :: Bool -> (Parser B.ByteString) -> ByteString -> [SplitResult]
splitOne _ _ Empty = []
splitOne bump bdry (Chunk x xs)
| bump = go 0 Nothing . parse bdry $ x
| otherwise = go 1 Nothing . parse bdry $ B.tail x
where
go n _ (Fail _ _ _)
| n < B.length x = let n' = n + 1
in go n' Nothing . parse bdry $ B.drop n' x
| otherwise = SplitChunk x : splitOne True bdry xs
go n ys (Done x' pfx) = [SplitChunk $ B.take n x,
Match pfx (chunk x' $ fromMaybe xs ys) $
isJust ys || B.length x' < B.length x n]
go n ys (Partial k) = goPartial n (fromMaybe xs ys) k
goPartial n (Chunk y ys) k = go n (Just ys) $ k y
goPartial n _ _ = go n Nothing $ Fail B.empty [] ""