@@ -118,6 +118,7 @@ module Data.Text.Lazy
118 , stripEnd
118 , stripEnd
119 , splitAt
119 , splitAt
121 , breakBy
122 , breakBy
123 , groupBy
124 , groupBy
@@ -127,8 +128,6 @@ module Data.Text.Lazy
127 -- ** Breaking into many substrings
128 -- ** Breaking into many substrings
128 -- $split
129 -- $split
132 , splitBy
131 , splitBy
133 , chunksOf
132 , chunksOf
134 -- , breakSubstring
133 -- , breakSubstring
@@ -166,7 +165,7 @@ module Data.Text.Lazy
167import Prelude (Char, Bool(..), Int, Maybe(..), String,
166import Prelude (Char, Bool(..), Int, Maybe(..), String,
168 Eq(..), Ord(..), Read(..), Show(..),
167 Eq(..), Ord(..), Read(..), Show(..),
169− (&&), (||), (+), (-), (.), ($), (++),
168+ (&&), (+), (-), (.), ($), (++),
170 div, flip, fromIntegral, not, otherwise)
169 div, flip, fromIntegral, not, otherwise)
171import qualified Prelude as P
170import qualified Prelude as P
172import Data.Int (Int64)
171import Data.Int (Int64)
@@ -175,11 +174,13 @@ import Data.Char (isSpace)
175import Data.Monoid (Monoid(..))
174import Data.Monoid (Monoid(..))
176import Data.String (IsString(..))
175import Data.String (IsString(..))
177import qualified Data.Text as T
176import qualified Data.Text as T
177+import qualified Data.Text.Internal as T
178import qualified Data.Text.Fusion.Common as S
178import qualified Data.Text.Fusion.Common as S
179import qualified Data.Text.Unsafe as T
179import qualified Data.Text.Unsafe as T
180import qualified Data.Text.Lazy.Fusion as S
180import qualified Data.Text.Lazy.Fusion as S
181import Data.Text.Lazy.Fusion (stream, unstream)
181import Data.Text.Lazy.Fusion (stream, unstream)
182−import Data.Text.Lazy.Internal
182+import Data.Text.Lazy.Internal (Text(..), chunk, empty, foldlChunks, foldrChunks)
183+import Data.Text.Internal (textP)
183import Data.Text.Lazy.Search (indices)
184import Data.Text.Lazy.Search (indices)
185instance Eq Text where
186instance Eq Text where
@@ -813,6 +814,42 @@ splitAt = loop
813 in (Chunk t ts', ts'')
814 in (Chunk t ts', ts'')
814 where len = fromIntegral (T.length t)
815 where len = fromIntegral (T.length t)
817+-- | /O(n+m)/ Find the first instance of @needle@ (which must be
818+-- non-'null') in @haystack@. The first element of the returned tuple
819+-- is the prefix of @haystack@ before @needle@ is matched. The second
820+-- is the remainder of @haystack@, starting with the match.
824+-- > break "::" "a::b::c" ==> ("a", "::b::c")
825+-- > break "/" "foobar" ==> ("foobar", "")
829+-- > append prefix match == haystack
830+-- > where (prefix, match) = break needle haystack
832+-- If you need to break a string by a substring repeatedly (e.g. you
833+-- want to break on every instance of a substring), use 'find'
834+-- instead, as it has lower startup overhead.
836+-- In (unlikely) bad cases, this function's time complexity degrades
838+break :: Text -> Text -> (Text, Text)
840+ | null pat = emptyError "break"
841+ | otherwise = case indices pat src of
845+ go _ Empty = (empty, empty)
846+ go x (Chunk c@(T.Text arr off len) cs)
847+ | y >= len = let (h,t) = go (x-fromIntegral len) cs
849+ | otherwise = (chunk (textP arr off y) empty,
850+ chunk (textP arr (off+y) (len-y)) cs)
851+ where y = fromIntegral x
816-- | /O(n)/ 'breakBy' is like 'spanBy', but the prefix returned is over
853-- | /O(n)/ 'breakBy' is like 'spanBy', but the prefix returned is over
817-- elements that fail the predicate @p@.
854-- elements that fail the predicate @p@.
818breakBy :: (Char -> Bool) -> Text -> (Text, Text)
855breakBy :: (Char -> Bool) -> Text -> (Text, Text)
@@ -913,57 +950,6 @@ split pat src0
913 split (singleton c) t = splitBy (==c) t
950 split (singleton c) t = splitBy (==c) t
916−-- | /O(m)*O(n)/ Break a 'Text' into pieces at most @k@ times,
917−-- treating the first 'Text' argument as the delimiter to break on,
918−-- and consuming the delimiter. The last element of the list contains
919−-- the remaining text after the number of times to split has been
920−-- reached. A value of zero or less for @k@ causes no splitting to
921−-- occur. An empty delimiter is invalid, and will cause an error to
926−-- > splitTimes 0 "//" "a//b//c" == ["a//b//c"]
927−-- > splitTimes 2 ":" "a:b:c:d:e" == ["a","b","c:d:e"]
928−-- > splitTimes 100 "???" "a????b" == ["a","?b"]
932−-- > intercalate s . splitTimes k s == id
933−splitTimes :: Int64 -- ^ Maximum number of times to split
934− -> Text -- ^ Text to split on
935− -> Text -- ^ Input text
937−splitTimes k pat src0
939− | l == 0 = emptyError "splitTimes"
940− | otherwise = go k src0
943− go !i src = search 0 src
946− | i == 0 || null s = [src] -- not found or limit reached
947− | pat `isPrefixOf` s = take n src : go (i-1) (drop l s)
948− | otherwise = search (n+1) (tail s)
949−{-# INLINE splitTimes #-}
951−-- | /O(m)*O(n)/ Break a 'Text' into pieces at most @k@ times, like
952−-- 'splitTimes', but start from the end of the input and work towards
957−-- > splitTimes 2 "::" "a::b::c::d::e" == ["a","b","c::d::e"]
958−-- > splitTimesEnd 2 "::" "a::b::c::d::e" == ["a::b::c","d","e"]
959−splitTimesEnd :: Int64 -- ^ Maximum number of times to split
960− -> Text -- ^ Text to split on
961− -> Text -- ^ Input text
963−splitTimesEnd k pat src =
964− L.reverse . L.map reverse $ splitTimes k (reverse pat) (reverse src)
965−{-# INLINE splitTimesEnd #-}
967-- | /O(n)/ Splits a 'Text' into components delimited by separators,
953-- | /O(n)/ Splits a 'Text' into components delimited by separators,
968-- where the predicate returns True for a separator element. The
954-- where the predicate returns True for a separator element. The
969-- resulting components do not contain the separators. Two adjacent
955-- resulting components do not contain the separators. Two adjacent