on master help sign in
apply the Data.Text.Search searching code to the wai findBound function for a significant speedup
commite4e95525bd1080815421d8422afddaca9e7b3bd9 authorjay groven <jgroven@lutsendata.com> dateWed 13 Jun 2012, 18:06 (14 years ago) parent810d0a4
3 files changed+123−2
wai-extra/Network/Wai/Parse.hs+6−1 wai-extra/Network/Wai/Search.hs+115 wai-extra/wai-extra.cabal+2−1
wai-extra/Network/Wai/Parse.hs+6−1
@@ -32,6 +32,7 @@ module Network.Wai.Parse
3232import qualified Data.ByteString as S
3333import qualified Data.ByteString.Lazy as L
3434import qualified Data.ByteString.Char8 as S8
35+import qualified Network.Wai.Search as Search
3536import Data.Word (Word8)
3637import Data.Bits
3738import Data.Maybe (fromMaybe)
@@ -280,7 +281,11 @@ data Bound = FoundBound S.ByteString S.ByteString
280281 deriving (Eq, Show)
281282
282283findBound :: S.ByteString -> S.ByteString -> Bound
283−findBound b bs = go [0..S.length bs - 1]
284+findBound b bs =
285+ case Search.index b bs of
286+ Nothing -> go [S.length bs - S.length b..S.length bs - 1]
287+ Just idx -> let (h, t) = S.splitAt idx bs in
288+ FoundBound h $ S.drop (S.length b) t
284289 where
285290 go [] = NoBound
286291 go (i:is)
wai-extra/Network/Wai/Search.hsnew file+115
@@ -0,0 +1,115 @@
1+{-# LANGUAGE BangPatterns, ScopedTypeVariables #-}
2+
3+-- |
4+-- Module : Network.Wai.Search
5+-- Copyright : (c) Bryan O'Sullivan 2009
6+--
7+-- License : BSD-style
8+-- Maintainer : bos@serpentine.com, rtomharper@googlemail.com,
9+-- duncan@haskell.org
10+-- Stability : experimental
11+-- Portability : GHC
12+--
13+-- Port of Data.Text.Search to ByteStrings, applied to the Wai framework
14+--
15+-- References:
16+--
17+-- * R. S. Boyer, J. S. Moore: A Fast String Searching Algorithm.
18+-- Communications of the ACM, 20, 10, 762-772 (1977)
19+--
20+-- * R. N. Horspool: Practical Fast Searching in Strings. Software -
21+-- Practice and Experience 10, 501-506 (1980)
22+--
23+-- * D. M. Sunday: A Very Fast Substring Search Algorithm.
24+-- Communications of the ACM, 33, 8, 132-142 (1990)
25+--
26+-- * F. Lundh: The Fast Search Algorithm.
27+-- <http://effbot.org/zone/stringlib.htm> (2006)
28+
29+module Network.Wai.Search
30+ (
31+ index
32+ , checkNeg
33+ , checkFound
34+ ) where
35+
36+import qualified Data.ByteString.Unsafe as SU
37+import qualified Data.ByteString as S
38+import Data.ByteString ( ByteString )
39+import Data.Word (Word64)
40+import Data.Bits ((.|.), (.&.), shiftL)
41+
42+data PairS a b = !a :*: !b
43+
44+-- | /O(n+m)/ Find the offset of @needle@ within @haystack@. The offset
45+-- returned represents the location within the haystack ByteString
46+--
47+-- In (unlikely) bad cases, this algorithm's complexity degrades
48+-- towards /O(n*m)/.
49+index :: ByteString -- ^ Substring to search for (@needle@)
50+ -> ByteString -- ^ Text to search in (@haystack@)
51+ -> Maybe Int
52+index needle haystack
53+ | nlen == 1 = scanOne (nindex 0)
54+ | nlen <= 0 || ldiff < 0 = Nothing
55+ | otherwise = scan 0
56+ where
57+ nlen = S.length needle
58+ hlen = S.length haystack
59+ ldiff = hlen - nlen
60+ nlast = nlen - 1
61+ z = nindex nlast
62+ nindex k = SU.unsafeIndex needle k
63+ hindex k = SU.unsafeIndex haystack k
64+ hindex' k | k == hlen = 0
65+ | otherwise = SU.unsafeIndex haystack k
66+ (mask :: Word64) :*: skip = buildTable 0 0 (nlen-2)
67+ buildTable !i !msk !skp
68+ | i >= nlast = (msk .|. swizzle z) :*: skp
69+ | otherwise = buildTable (i+1) (msk .|. swizzle c) skp'
70+ where c = nindex i
71+ skp' | c == z = nlen - i - 2
72+ | otherwise = skp
73+ swizzle k = 1 `shiftL` (fromIntegral k .&. 0x3f)
74+ scan !i
75+ | i > ldiff = Nothing
76+ | c == z && candidateMatch 0 = Just i
77+ | otherwise = scan (i + delta)
78+ where c = hindex (i + nlast)
79+ candidateMatch !j
80+ | j >= nlast = True
81+ | hindex (i+j) /= nindex j = False
82+ | otherwise = candidateMatch (j+1)
83+ delta | nextInPattern = nlen + 1
84+ | c == z = skip + 1
85+ | otherwise = 1
86+ nextInPattern = mask .&. swizzle (hindex' (i+nlen)) == 0
87+ scanOne c = loop 0
88+ where loop !i | i >= hlen = Nothing
89+ | hindex i == c = Just i
90+ | otherwise = loop (i+1)
91+{-# INLINE index #-}
92+
93+-- It's not likely that one arbitrary string will ever be a substring of some
94+-- other string, so this is pretty much a negative test
95+{-
96+checkNeg :: ByteString -> ByteString -> Bool
97+checkNeg n h
98+ | S.null n = True
99+ | otherwise = (S.findSubstring n h) == (index n h)
100+ -}
101+
102+-- Make sure that index works the same as ByteString.findSubstring when we
103+-- force matches
104+{-
105+checkFound :: ByteString -> Int -> Int -> Bool
106+checkFound bs offset len
107+ | S.null n = True -- cop out
108+ | otherwise = case index n bs of
109+ Nothing -> False
110+ _ -> checkNeg n bs
111+ where
112+ n = S.take len $ S.drop offset bs
113+ -}
114+
115+
wai-extra/wai-extra.cabal+2−1
@@ -52,7 +52,8 @@ Library
5252 Network.Wai.Middleware.Rewrite
5353 Network.Wai.Middleware.Vhost
5454 Network.Wai.Parse
55− ghc-options: -Wall
55+ Network.Wai.Search
56+ ghc-options: -Wall -O2
5657
5758
5859test-suite tests