on master help sign in
Write a new version of replace that is 2.2x faster
3cf8564Bryan O'Sullivan
Data/Text.hs+26
@@ -73,6 +73,7 @@ module Data.Text
73 , transpose
73 , transpose
74 , reverse
74 , reverse
75 , replace
75 , replace
76+ , replace2
76
77
77 -- ** Case conversion
78 -- ** Case conversion
78 -- $case
79 -- $case
@@ -623,6 +624,31 @@ replace :: Text -- ^ Text to search for
623replace s d = intercalate d . splitOn s
624replace s d = intercalate d . splitOn s
624{-# INLINE replace #-}
625{-# INLINE replace #-}
625
626
627+replace2 :: Text -> Text -> Text -> Text
628+replace2 needle@(Text _ _ neeLen)
629+ r@(Text repArr repOff repLen)
630+ haystack@(Text hayArr hayOff hayLen)
631+ | neeLen == 0 = emptyError "replace2"
632+ | len < 0 = overflowError "replace2"
633+ | len == 0 = empty
634+ | L.null ixs = haystack
635+ | otherwise = Text (A.run x) 0 len
636+ where
637+ ixs = indices needle haystack
638+ cnt = L.length ixs
639+ len = hayLen - (neeLen - repLen) * cnt
640+ x = do
641+ marr <- A.new len
642+ let loop is0@(i:is) o d = do
643+ let d0 = d + i - o
644+ d1 = d0 + repLen
645+ A.copyI marr d hayArr (hayOff+o) d0
646+ A.copyI marr d0 repArr repOff d1
647+ loop is (i + neeLen) d1
648+ loop [] o d = A.copyI marr d hayArr (hayOff+o) len
649+ loop ixs 0 0
650+ return marr
651+
626-- ----------------------------------------------------------------------------
652-- ----------------------------------------------------------------------------
627-- ** Case conversions (folds)
653-- ** Case conversions (folds)
628
654
benchmarks/haskell/Benchmarks/Replace.hs+1
@@ -28,6 +28,7 @@ benchmark fp pat sub = do
28 !b = T.encodeUtf8 t
28 !b = T.encodeUtf8 t
29 return $ bgroup "Replace" [
29 return $ bgroup "Replace" [
30 bench "Text" $ nf (T.length . T.replace tpat tsub) t
30 bench "Text" $ nf (T.length . T.replace tpat tsub) t
31+ , bench "Text2" $ nf (T.length . T.replace2 tpat tsub) t
31 , bench "ByteString" $ nf (BL.length . B.replace bpat bsub) b
32 , bench "ByteString" $ nf (BL.length . B.replace bpat bsub) b
32 , bench "LazyText" $ nf (TL.length . TL.replace tlpat tlsub) tl
33 , bench "LazyText" $ nf (TL.length . TL.replace tlpat tlsub) tl
33 , bench "LazyByteString" $ nf (BL.length . BL.replace blpat blsub) bl
34 , bench "LazyByteString" $ nf (BL.length . BL.replace blpat blsub) bl
tests/Tests/Properties.hs+6−1
@@ -234,12 +234,16 @@ t_reverse_short n = L.reverse `eqP` (unpackS . S.reverse . shorten n . S.stream)
234
234
235t_replace s d = (L.intercalate d . splitOn s) `eqP`
235t_replace s d = (L.intercalate d . splitOn s) `eqP`
236 (unpackS . T.replace (T.pack s) (T.pack d))
236 (unpackS . T.replace (T.pack s) (T.pack d))
237+t_replace2 (NotEmpty s0) (NotEmpty d0) (NotEmpty w0) =
238+ T.replace s d w =^= T.replace2 s d w
239+ where s = f s0; d = f d0; w = f w0
240+ f = T.tail . T.pack
237tl_replace s d = (L.intercalate d . splitOn s) `eqP`
241tl_replace s d = (L.intercalate d . splitOn s) `eqP`
238 (unpackS . TL.replace (TL.pack s) (TL.pack d))
242 (unpackS . TL.replace (TL.pack s) (TL.pack d))
239
243
240splitOn :: (Eq a) => [a] -> [a] -> [[a]]
244splitOn :: (Eq a) => [a] -> [a] -> [[a]]
241splitOn pat src0
245splitOn pat src0
242− | l == 0 = error "empty"
246+ | l == 0 = error "splitOn: empty"
243 | otherwise = go src0
247 | otherwise = go src0
244 where
248 where
245 l = length pat
249 l = length pat
@@ -896,6 +900,7 @@ tests =
896 testProperty "tl_reverse" tl_reverse,
900 testProperty "tl_reverse" tl_reverse,
897 testProperty "t_reverse_short" t_reverse_short,
901 testProperty "t_reverse_short" t_reverse_short,
898 testProperty "t_replace" t_replace,
902 testProperty "t_replace" t_replace,
903+ testProperty "t_replace2" t_replace2,
899 testProperty "tl_replace" tl_replace,
904 testProperty "tl_replace" tl_replace,
900
905
901 testGroup "case conversion" [
906 testGroup "case conversion" [