on master help sign in
Finish off the search code, and hide it.

--HG-- extra : convert_revision : 25e274adc6811abb9d0a8d21d8715b13ccf59282

commitdce830fb0e309f8bdebeea1e1c624c47485d7a14 authorBryan O'Sullivan <bos@serpentine.com> dateMon 24 Aug 2009, 06:59 (17 years ago) parent34d4996
3 files changed+86−22
Data/Text/Search.hs+77−20 tests/Properties.hs+8−1 text.cabal+1−1
Data/Text/Search.hs+77−20
@@ -1,25 +1,62 @@
11{-# LANGUAGE BangPatterns, ScopedTypeVariables #-}
22
3−module Data.Text.Search where
3+-- |
4+-- Module : Data.Text.Search
5+-- Copyright : (c) Bryan O'Sullivan 2009
6+--
7+-- License : BSD-style
8+-- Maintainer : bos@serpentine.com, rtharper@aftereternity.co.uk,
9+-- duncan@haskell.org
10+-- Stability : experimental
11+-- Portability : GHC
12+--
13+-- Fast substring search for 'Text', based on work by Boyer, Moore,
14+-- Horspool, Sunday, and Lundh.
15+--
16+-- References:
17+--
18+-- * R. S. Boyer, J. S. Moore: A Fast String Searching Algorithm.
19+-- Communications of the ACM, 20, 10, 762-772 (1977)
20+--
21+-- * R. N. Horspool: Practical Fast Searching in Strings. Software -
22+-- Practice and Experience 10, 501-506 (1980)
23+--
24+-- * D. M. Sunday: A Very Fast Substring Search Algorithm.
25+-- Communications of the ACM, 33, 8, 132-142 (1990)
26+--
27+-- * F. Lundh: The Fast Search Algorithm.
28+-- <http://effbot.org/zone/stringlib.htm> (2006)
29+module Data.Text.Search
30+ (
31+ indices
32+ , slowIndices
33+ ) where
434
5−import qualified Data.Text as T ()
35+import qualified Data.Text as T
636import qualified Data.Text.Array as A
737import Data.Word (Word64)
838import Data.Text.Internal (Text(..))
939import Data.Bits ((.|.), (.&.))
1040import Data.Text.UnsafeShift (shiftL)
41+import Data.Text.Unsafe (iter_)
1142
12−indices :: Text -> Text -> [Int]
43+-- | /O(n+m)/ Find the offsets of all non-overlapping indices of
44+-- @needle@ within @haystack@. In (unlikely) bad cases, this
45+-- algorithm's complexity degenerates towards /O(n*m)/.
46+indices :: Text -- ^ Substring to search for (@needle@)
47+ -> Text -- ^ Text to search in (@haystack@)
48+ -> [Int]
1349indices _needle@(Text narr noff nlen) _haystack@(Text harr hoff hlen)
14− | ldiff < 0 = []
15− | otherwise = outer 0
50+ | nlen == 1 = scanOne (nindex 0)
51+ | nlen <= 0 || ldiff < 0 = []
52+ | otherwise = scan 0
1653 where
17− ldiff = hlen - nlen
18− nlast = nlen - 1
19− z = nindex nlast
54+ ldiff = hlen - nlen
55+ nlast = nlen - 1
56+ z = nindex nlast
2057 nindex k = A.unsafeIndex narr (noff+k)
2158 hindex k = A.unsafeIndex harr (hoff+k)
22− (mask :: Word64, skip :: Int) = buildTable 0 0 (nlen-2)
59+ (mask :: Word64, skip) = buildTable 0 0 (nlen-2)
2360 buildTable !i !msk !skp
2461 | i >= nlast = (msk .|. swizzle z, skp)
2562 | otherwise = buildTable (i+1) (msk .|. swizzle c) skp'
@@ -27,18 +64,38 @@ indices _needle@(Text narr noff nlen) _haystack@(Text harr hoff hlen)
2764 skp' | c == z = nlen - i - 2
2865 | otherwise = skp
2966 swizzle k = 1 `shiftL` (fromEnum k .&. 0x3f)
30− outer !i
31− | i > ldiff = []
32− | c == z = if candidateMatch 0
33− then i : outer (i + nlast)
34− else if nextInPattern
35− then outer (i + nlen)
36− else outer (i + skip)
37− | nextInPattern = outer (i + nlen)
38− | otherwise = outer (i + 1)
39− where c = hindex (i+nlast)
67+ scan !i
68+ | i > ldiff = []
69+ | c == z && candidateMatch 0 = i : scan (i + nlast)
70+ | otherwise = scan (i + delta)
71+ where c = hindex (i + nlast)
4072 candidateMatch j
4173 | j >= nlast = True
4274 | hindex (i+j) /= nindex j = False
4375 | otherwise = candidateMatch (j+1)
44− nextInPattern = (mask .&. swizzle (hindex (i+nlen))) == 0
76+ delta
77+ | nextInPattern = nlen + 1
78+ | c == z = skip + 1
79+ | otherwise = 1
80+ nextInPattern = (mask .&. swizzle (hindex (i+nlen))) == 0
81+ scanOne c = loop 0
82+ where loop !i | i >= hlen = []
83+ | hindex i == c = i : loop (i+1)
84+ | otherwise = loop (i+1)
85+{-# INLINE indices #-}
86+
87+-- | /O(n*m)/ Find the offsets of all non-overlapping indices of
88+-- @needle@ within @haystack@. Provided purely as a slower, reliable
89+-- counterpart to 'indices'.
90+slowIndices :: Text -- ^ Substring to search for (@needle@)
91+ -> Text -- ^ Text to search in (@haystack@)
92+ -> [Int]
93+slowIndices needle@(Text _narr _noff nlen) haystack@(Text harr hoff hlen)
94+ | T.null needle = []
95+ | otherwise = scan 0
96+ where
97+ scan i | i >= hlen = []
98+ | needle `T.isPrefixOf` t = i : scan (i+nlen)
99+ | otherwise = scan (i+d)
100+ where t = Text harr (hoff+i) (hlen-i)
101+ d = iter_ haystack i
tests/Properties.hs+8−1
@@ -26,6 +26,7 @@ import Prelude hiding (replicate)
2626import System.IO.Unsafe (unsafePerformIO)
2727import Test.Framework (defaultMain, testGroup)
2828import Test.Framework.Providers.QuickCheck (testProperty)
29+import Data.Text.Search
2930
3031import QuickCheckUtils (NotEmpty(..))
3132
@@ -486,6 +487,10 @@ sf_zipWith p c s = (L.zipWith c (L.filter p s) . L.filter p) `eqP` (unpackS . S
486487t_zipWith c s = L.zipWith c s `eqP` (unpackS . T.zipWith c (packS s))
487488tl_zipWith c s = L.zipWith c s `eqP` (unpackS . TL.zipWith c (packS s))
488489
490+t_indices = unsquare (\s -> slowIndices s `eq` indices s)
491+t_indices_occurs t = unsquare (\ts -> let s = T.intercalate t ts
492+ in slowIndices t s == indices t s)
493+
489494-- Bit shifts.
490495shiftL w = forAll (choose (0,width-1)) $ \k -> Bits.shiftL w k == U.shiftL w k
491496 where width = round (log (fromIntegral m) / log 2 :: Double)
@@ -836,7 +841,9 @@ tests = [
836841 testProperty "t_elemIndices" t_elemIndices,
837842 testProperty "tl_elemIndices" tl_elemIndices,
838843 testProperty "t_count" t_count,
839− testProperty "tl_count" tl_count
844+ testProperty "tl_count" tl_count,
845+ testProperty "t_indices" t_indices,
846+ testProperty "t_indices_occurs" t_indices_occurs
840847 ],
841848
842849 testGroup "zips" [
text.cabal+1−1
@@ -22,7 +22,6 @@ library
2222 Data.Text.Foreign
2323 Data.Text.Lazy
2424 Data.Text.Lazy.Encoding
25− Data.Text.Search
2625 other-modules:
2726 Data.Text.Array
2827 Data.Text.Encoding.Fusion
@@ -38,6 +37,7 @@ library
3837 Data.Text.Lazy.Encoding.Fusion
3938 Data.Text.Lazy.Fusion
4039 Data.Text.Lazy.Internal
40+ Data.Text.Search
4141 Data.Text.Unsafe
4242 Data.Text.UnsafeChar
4343 Data.Text.UnsafeShift