on master help sign in
Rewrite find to use indices. New test is broken, alas.

--HG-- extra : convert_revision : 0f69b785d92da6da34b16c5e0e09d52e6a988355

commit7537b75a688ad5062a31d34a2c865d284c563cb9 authorBryan O'Sullivan <bos@serpentine.com> dateTue 6 Oct 2009, 18:38 (17 years ago) parent5f475ca
2 files changed+21−18
Data/Text.hs+11−13 tests/Properties.hs+10−5
Data/Text.hs+11−13
@@ -1054,7 +1054,7 @@ filter :: (Char -> Bool) -> Text -> Text
10541054filter p t = unstream (S.filter p (stream t))
10551055{-# INLINE filter #-}
10561056
1057−-- /O(n*m)/ Find all non-overlapping instances of @needle@ in
1057+-- /O(n+m)/ Find all non-overlapping instances of @needle@ in
10581058-- @haystack@. The first element of the returned tuple is the prefix
10591059-- of @haystack@ before any matches of @needle@. The second element
10601060-- of the tuple is a list of non-overlapping substrings from
@@ -1071,20 +1071,18 @@ filter p t = unstream (S.filter p (stream t))
10711071--
10721072-- > concat (prefix : matches) == haystack
10731073-- > where (prefix, matches) = find needle haystack
1074+--
1075+-- In (unlikely) bad cases, this function's time complexity
1076+-- degenerates towards /O(n*m)/.
10741077find :: Text -> Text -> (Text, [Text])
1075−find pat@(Text _ _ plen) src@(Text sarr soff slen)
1076− | plen <= 0 = emptyError "find"
1077− | otherwise = (h,t)
1078+find pat src@(Text arr off len)
1079+ | null pat = emptyError "find"
1080+ | otherwise = case indices pat src of
1081+ [] -> (src, [])
1082+ (x:xs) -> (textP arr off x, go x xs)
10781083 where
1079− (h:t) = go 0 (search 0)
1080− go k (x:xs) = Text sarr k (x-k) : go x xs
1081− go _ [] = []
1082− search i
1083− | i >= slen = [slen] -- not found
1084− | pat `isPrefixOf` s = i : search (i+plen)
1085− | otherwise = search (i+d)
1086− where d = iter_ src i
1087− s = Text sarr (soff+i) (slen-i)
1084+ go !s (x:xs) = textP arr (s+off) (x-s) : go x xs
1085+ go s _ = [textP arr (s+off) (len-s)]
10881086{-# INLINE find #-}
10891087
10901088-------------------------------------------------------------------------------
tests/Properties.hs+10−5
@@ -2,7 +2,7 @@
22 TypeSynonymInstances #-}
33{-# OPTIONS_GHC -fno-enable-rewrite-rules #-}
44
5−import Test.QuickCheck
5+import Test.QuickCheck hiding (evaluate)
66import Text.Show.Functions ()
77
88import qualified Data.Bits as Bits (shiftL, shiftR)
@@ -15,7 +15,7 @@ import Data.Word (Word8, Word16, Word32)
1515import qualified Data.Text as T
1616import qualified Data.Text.Lazy as TL
1717import qualified Data.Text.Encoding as E
18−import Control.Exception (SomeException, try)
18+import Control.Exception (SomeException, evaluate, try)
1919import qualified Data.Text.Fusion as S
2020import qualified Data.Text.Fusion.Common as S
2121import Data.Text.Fusion.Size
@@ -38,8 +38,8 @@ import QuickCheckUtils (NotEmpty(..), small)
3838(=^=) :: (Eq a, Show a) => a -> a -> Bool
3939{-# NOINLINE (=^=) #-}
4040i =^= j = unsafePerformIO $ do
41− x <- try (return $! i)
42− y <- try (return $! j)
41+ x <- try (evaluate i)
42+ y <- try (evaluate j)
4343 case (x,y) of
4444 (Left (_ :: SomeException), Left (_ :: SomeException))
4545 -> return True
@@ -398,7 +398,11 @@ tl_inits = L.inits `eqP` (map unpackS . TL.inits)
398398t_tails = L.tails `eqP` (map unpackS . T.tails)
399399tl_tails = L.tails `eqP` (map unpackS . TL.tails)
400400
401−t_split_split p = T.split p `eq` Slow.split p
401+findSplit s t = let (x,xs) = T.find s t
402+ in x : L.map (T.drop (T.length s)) xs
403+
404+t_findSplit s = T.split s `eq` findSplit s
405+t_split_split s = T.split s `eq` Slow.split s
402406t_split_i (NotEmpty t) = id `eq` (T.intercalate t . T.split t)
403407tl_split_i (NotEmpty t) = id `eq` (TL.intercalate t . TL.split t)
404408t_splitTimes_i k (NotEmpty t) = id `eq` (T.intercalate t . T.splitTimes k t)
@@ -802,6 +806,7 @@ tests = [
802806 ],
803807
804808 testGroup "breaking many" [
809+ testProperty "t_findSplit" t_findSplit,
805810 testProperty "t_split_split" t_split_split,
806811 testProperty "t_split_i" t_split_i,
807812 testProperty "t_splitTimes_i" t_splitTimes_i,