import Data.Char ( ord )
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as BC
import qualified Data.ByteString.Internal as BI
import Test.QuickCheck

-- | betweenLinesPS returns the B.ByteString between the two lines given,
-- or Nothing if they do not appear.
betweenLinesPS :: B.ByteString -> B.ByteString
               -> B.ByteString -> Maybe B.ByteString
betweenLinesPS start end ps =
  case break (start ==) (linesPS1 ps) of
  -- replace this call here ^^^^^ with linesPS2
  -- and it crashes
    (_, _:rest@(bs1:_)) ->
      case BI.toForeignPtr bs1 of
        (ps1, s1, _) ->
          case break (end ==) rest of
            (_, bs2:_) ->
              case BI.toForeignPtr bs2 of
                (_, s2, _) -> Just $ BI.fromForeignPtr ps1 s1 (s2 - s1)
            _ -> Nothing
    _ -> Nothing

{-# INLINE linesPS1 #-}
linesPS1 :: B.ByteString -> [B.ByteString]
linesPS1 ps
     | B.null ps = [B.empty]
     | otherwise = BC.split '\n' ps

{-# INLINE linesPS2 #-}
linesPS2 :: B.ByteString -> [B.ByteString]
linesPS2 ps = case search ps of
    Nothing -> [ps]
    Just n -> B.take n ps : linesPS2 (B.drop (n + 1) ps)
  where
    search = BC.elemIndex '\n'

instance Arbitrary B.ByteString where
  arbitrary = B.pack <$> arbitrary

-- just to check that linesPS1 and linePS2 really do the same
prop_linesPS x = linesPS1 x == linesPS2 x

main = do
  quickCheck prop_linesPS
  print $ betweenLinesPS (BC.pack "a") (BC.pack "b") $ BC.pack "a\n\n\nb"
