@@ -29,8 +29,8 @@ module Data.ParserCombinators.Attoparsec.FastSet
2929 , fromSet
3030 ) where
3131
32+import Data.Bits ((.&.), (.|.), shiftR)
3233import qualified Data.ByteString as B
33−-- import Data.ByteString.Char8 (pack)
3434import qualified Data.ByteString.Internal as I
3535import qualified Data.ByteString.Unsafe as U
3636import Data.Word (Word8)
@@ -41,13 +41,12 @@ data FastSet = Sorted { fromSet :: {-# UNPACK #-} !B.ByteString }
4141 deriving (Eq, Ord)
4242
4343instance Show FastSet where
44− show (Sorted s) = "FastSet " ++ show s
45− show (Table t) = "FastSet " ++ fromTable t
44+ show _ = "FastSet"
4645
4746-- | The lower bound on the size of a lookup table. We choose this to
4847-- balance table density against performance.
4948tableCutoff :: Int
50−tableCutoff = 32
49+tableCutoff = 8
5150
5251-- | Create a set.
5352set :: B.ByteString -> FastSet
@@ -56,7 +55,9 @@ set s | B.length s < tableCutoff = Sorted . B.sort $ s
5655
5756-- | Check the set for membership.
5857memberWord8 :: Word8 -> FastSet -> Bool
59−memberWord8 w (Table t) = U.unsafeIndex t (fromIntegral w) == entry
58+memberWord8 w (Table t) =
59+ let i = fromIntegral w
60+ in U.unsafeIndex t (i `shiftR` 3) .&. fromIntegral (i .&. 7) == 1
6061memberWord8 w (Sorted s) = search 0 (B.length s - 1)
6162 where search lo hi
6263 | hi < lo = False
@@ -72,29 +73,16 @@ memberWord8 w (Sorted s) = search 0 (B.length s - 1)
7273memberChar :: Char -> FastSet -> Bool
7374memberChar c = memberWord8 (I.c2w c)
7475
75−-- | The value in a table that indicates that a character is not
76−-- present. We avoid NUL to make the table representation printable.
77−noEntry :: Word8
78−noEntry = 0x5f
79−
80−-- | The value in a table that indicates that a character is present.
81−-- We use a printable character for readability.
82−entry :: Word8
83−entry = 0x21
84−
8576mkTable :: B.ByteString -> B.ByteString
86−mkTable s = I.unsafeCreate 256 $ \t -> do
87− I.memset t noEntry 256
77+mkTable s = I.unsafeCreate 32 $ \t -> do
78+ I.memset t 0 32
8879 U.unsafeUseAsCStringLen s $ \(p, l) ->
8980 let loop n | n == l = return ()
9081 | otherwise = do
9182 c <- peekByteOff p n :: IO Word8
92− pokeByteOff t (fromIntegral c) entry
83+ let i = fromIntegral c
84+ o = i `shiftR` 3
85+ prev <- peekByteOff t o
86+ pokeByteOff t o (prev .|. (i .&. 7))
9387 loop (n + 1)
9488 in loop 0
95−
96−-- | Turn the table representation into a string, for debugging.
97−fromTable :: B.ByteString -> String
98−fromTable = snd . B.foldr go (0xff, [])
99− where go c (n, cs) | c == noEntry = flip (,) cs $! n - 1
100− | otherwise = flip (,) (I.w2c n:cs) $! n - 1