on master help sign in
Flesh out the implementation of lazy indices, with tests.

Alas, it has a bug. --HG-- extra : convert_revision : b38cfbaef2c9b2a832a7c83c1952eca05ecd7c2a

commitd3faa5d73bcecdb5cfed84878bc713c27c24f54e authorBryan O'Sullivan <bos@serpentine.com> dateWed 7 Oct 2009, 01:50 (17 years ago) parentebad96d
3 files changed+49−16
Data/Text/Lazy/Search.hs+30−16 tests/Properties.hs+5 tests/SlowFunctions.hs+14
Data/Text/Lazy/Search.hs+30−16
@@ -36,6 +36,7 @@ import qualified Data.Text.Array as A
3636import Data.Int (Int64)
3737import Data.Word (Word16, Word64)
3838import qualified Data.Text.Internal as T
39+import qualified Data.Text as T
3940import Data.Text.Fusion.Internal (PairS(..))
4041import Data.Text.Lazy.Internal (Text(..))
4142import Data.Bits ((.|.), (.&.))
@@ -49,12 +50,12 @@ import Data.Text.UnsafeShift (shiftL)
4950indices :: Text -- ^ Substring to search for (@needle@)
5051 -> Text -- ^ Text to search in (@haystack@)
5152 -> [Int64]
52−indices needle Empty = []
53−indices needle@(Chunk n ns) haystack@(Chunk c cs)
53+indices needle@(Chunk n ns) haystack@(Chunk k ks)
5454 | nlen <= 0 || ldiff < 0 = []
55− | otherwise = scan 0 0 c cs
55+ | nlen == 1 = scanOne (nindex 0) 0 k ks
56+ | otherwise = scan 0 0 k ks
5657 where
57− scan !g !i x@(T.Text harr hoff l) xs
58+ scan !g !i x@(T.Text _ _ l) xs
5859 | g > ldiff = []
5960 | i >= m = case xs of
6061 Empty -> []
@@ -79,17 +80,28 @@ indices needle@(Chunk n ns) haystack@(Chunk c cs)
7980 nlast = nlen - 1
8081 nindex = index n ns
8182 z = foldChunks fin 0 needle
82− where fin _ (T.Text narr noff nlen) = A.unsafeIndex narr (noff+nlen-1)
83+ where fin _ (T.Text farr foff flen) = A.unsafeIndex farr (foff+flen-1)
8384 mask :*: skip = buildTable needle
85+ scanOne c i (T.Text oarr ooff olen) os = go 0
86+ where
87+ go h | h >= olen = case os of
88+ Empty -> []
89+ Chunk y ys -> scanOne c (i+fromIntegral olen) y ys
90+ | on == c = i + fromIntegral h : go (h+1)
91+ | otherwise = go (h+1)
92+ where on = A.unsafeIndex oarr (ooff+h)
93+indices _ _ = []
8494
8595index :: T.Text -> Text -> Int64 -> Word16
8696index (T.Text arr off len) xs i
87− | j <= len = A.unsafeIndex arr (off+j)
97+ | j < len = A.unsafeIndex arr (off+j)
8898 | otherwise = case xs of
89− Empty -> error "empty"
99+ Empty | j == len -> 0
100+ | otherwise -> error "empty"
90101 Chunk c cs -> index c cs (i-fromIntegral len)
91102 where j = fromIntegral i
92103
104+swizzle :: Word16 -> Word64
93105swizzle k = 1 `shiftL` (fromIntegral k .&. 0x3f)
94106
95107foldChunks :: (a -> T.Text -> a) -> a -> Text -> a
@@ -97,23 +109,25 @@ foldChunks _ z Empty = z
97109foldChunks f z (Chunk c cs) = let z' = f z c
98110 in z' `seq` foldChunks f z' cs
99111
112+wordLength :: Text -> Int64
100113wordLength = foldChunks sumLength 0
101114 where sumLength i (T.Text _ _ l) = i + fromIntegral l
102115
103116buildTable :: Text -> PairS Word64 Int64
104117buildTable Empty = 0 :*: 0
105−buildTable needle@(Chunk c cs) = outer c cs 0 0 (nlen-2)
118+buildTable needle@(Chunk k ks) = outer k ks 0 0 0 (nlen-2)
106119 where
107− outer x@(T.Text xarr xoff xlen) xs = go
120+ outer (T.Text xarr xoff xlen) xs = go
108121 where
109− go !i !mask !skip
110− | i >= xlen = case xs of
111− Empty -> (mask .|. swizzle z) :*: skip
112− Chunk y ys -> outer y ys 0 mask skip
113− | otherwise = go (i+1) (mask .|. swizzle c) skip'
122+ go !(g::Int64) !i !mask !skip
123+ | i >= xlast = case xs of
124+ Empty -> (mask .|. swizzle z) :*: skip
125+ Chunk y ys -> outer y ys g 0 mask skip
126+ | otherwise = go (g+1) (i+1) (mask .|. swizzle c) skip'
114127 where c = A.unsafeIndex xarr (xoff+i)
115− skip' | c == z = nlen - fromIntegral i - 2
128+ skip' | c == z = nlen - fromIntegral g - 2
116129 | otherwise = skip
130+ xlast = xlen - 1
117131 nlen = wordLength needle
118132 z = foldChunks fin 0 needle
119− where fin _ (T.Text narr noff nlen) = A.unsafeIndex narr (noff+nlen-1)
133+ where fin _ (T.Text farr foff flen) = A.unsafeIndex farr (foff+flen-1)
tests/Properties.hs+5
@@ -28,6 +28,7 @@ import System.IO.Unsafe (unsafePerformIO)
2828import Test.Framework (defaultMain, testGroup)
2929import Test.Framework.Providers.QuickCheck (testProperty)
3030import Data.Text.Search (indices)
31+import qualified Data.Text.Lazy.Search as S (indices)
3132import qualified SlowFunctions as Slow
3233
3334import QuickCheckUtils (NotEmpty(..), small)
@@ -486,7 +487,9 @@ 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+slow_indices = unsquare (\s -> (map fromIntegral . Slow.indices (T.pack s)) `eq` (Slow.lazyIndices (TL.pack s) . TL.fromChunks . (:[])))
489491t_indices = unsquare (\s -> Slow.indices s `eq` indices s)
492+tl_indices = unsquare (\s -> Slow.lazyIndices s `eq` S.indices s)
490493t_indices_occurs t = unsquare (\ts -> let s = T.intercalate t ts
491494 in Slow.indices t s == indices t s)
492495
@@ -837,7 +840,9 @@ tests = [
837840 testProperty "t_findIndex" t_findIndex,
838841 testProperty "t_count" t_count,
839842 testProperty "tl_count" tl_count,
843+ testProperty "slow_indices" slow_indices,
840844 testProperty "t_indices" t_indices,
845+ testProperty "tl_indices" tl_indices,
841846 testProperty "t_indices_occurs" t_indices_occurs
842847 ],
843848
tests/SlowFunctions.hs+14
@@ -3,10 +3,13 @@
33module SlowFunctions
44 (
55 indices
6+ , lazyIndices
67 , split
78 ) where
89
10+import Data.Int (Int64)
911import qualified Data.Text as T
12+import qualified Data.Text.Lazy as L
1013import Data.Text.Internal (Text(..))
1114import Data.Text.Unsafe (iter_, unsafeHead, unsafeTail)
1215
@@ -23,6 +26,17 @@ indices needle@(Text _narr _noff nlen) haystack@(Text harr hoff hlen)
2326 where t = Text harr (hoff+i) (hlen-i)
2427 d = iter_ haystack i
2528
29+lazyIndices :: L.Text -> L.Text -> [Int64]
30+lazyIndices needle haystack
31+ | L.null needle = []
32+ | otherwise = scan 0 haystack
33+ where
34+ scan !i hay
35+ | L.null hay = []
36+ | needle `L.isPrefixOf` hay = i : scan (i+n) (L.drop n hay)
37+ | otherwise = scan (i+1) (L.tail hay)
38+ n = L.length needle
39+
2640split :: Text -- ^ Text to split on
2741 -> Text -- ^ Input text
2842 -> [Text]