@@ -0,0 +1,79 @@
1+{-# LANGUAGE BangPatterns, ScopedTypeVariables #-}
4+-- Module : Data.Text.Lazy.Search
5+-- Copyright : (c) Bryan O'Sullivan 2009
8+-- Maintainer : bos@serpentine.com, rtharper@aftereternity.co.uk,
10+-- Stability : experimental
13+-- Fast substring search for 'Text', based on work by Boyer, Moore,
14+-- Horspool, Sunday, and Lundh.
18+-- * R. S. Boyer, J. S. Moore: A Fast String Searching Algorithm.
19+-- Communications of the ACM, 20, 10, 762-772 (1977)
21+-- * R. N. Horspool: Practical Fast Searching in Strings. Software -
22+-- Practice and Experience 10, 501-506 (1980)
24+-- * D. M. Sunday: A Very Fast Substring Search Algorithm.
25+-- Communications of the ACM, 33, 8, 132-142 (1990)
27+-- * F. Lundh: The Fast Search Algorithm.
28+-- <http://effbot.org/zone/stringlib.htm> (2006)
30+module Data.Text.Lazy.Search
35+import qualified Data.Text.Array as A
36+import Data.Int (Int64)
37+import Data.Word (Word64)
38+import qualified Data.Text.Internal as T
39+import Data.Text.Fusion.Internal (PairS(..))
40+import Data.Text.Lazy.Internal (Text(..))
41+import Data.Bits ((.|.), (.&.))
42+import Data.Text.UnsafeShift (shiftL)
44+-- | /O(n+m)/ Find the offsets of all non-overlapping indices of
45+-- @needle@ within @haystack@.
47+-- In (unlikely) bad cases, this algorithm's complexity degrades
49+indices :: Text -- ^ Substring to search for (@needle@)
50+ -> Text -- ^ Text to search in (@haystack@)
54+foldChunks :: (a -> T.Text -> a) -> a -> Text -> a
55+foldChunks _ z Empty = z
56+foldChunks f z (Chunk c cs) = let z' = f z c
57+ in z' `seq` foldChunks f z' cs
59+wordLength = foldChunks sumLength 0
60+ where sumLength i (T.Text _ _ l) = i + fromIntegral l
62+buildTable :: Text -> PairS Word64 Int64
63+buildTable Empty = 0 :*: 0
64+buildTable needle@(Chunk c cs) = outer c cs 0 0 (nlen-2)
66+ outer x@(T.Text xarr xoff xlen) xs = go
69+ | i >= xlen = case xs of
70+ Empty -> (mask .|. swizzle z) :*: skip
71+ Chunk y ys -> outer y ys 0 mask skip
72+ | otherwise = go (i+1) (mask .|. swizzle c) skip'
73+ where c = A.unsafeIndex xarr (xoff+i)
74+ skip' | c == z = nlen - fromIntegral i - 2
76+ swizzle k = 1 `shiftL` (fromIntegral k .&. 0x3f)
77+ nlen = wordLength needle
78+ z = foldChunks fin 0 needle
79+ where fin _ (T.Text narr noff nlen) = A.unsafeIndex narr (noff+nlen-1)