on master help sign in
Use blaze-builder to construct strings slightly more quickly.
commite32125bf331758a9273ddddf52e7de394cec476a authorBryan O'Sullivan <bos@mailrank.com> dateThu 27 Jan 2011, 15:17 (16 years ago) parenta8ea7ae
1 file changed+31−24
Data/Aeson/Parser.hs+31−24
Data/Aeson/Parser.hs+31−24
@@ -15,6 +15,9 @@ module Data.Aeson.Parser
1515 , value
1616 ) where
1717
18+import Blaze.ByteString.Builder (fromByteString, toByteString)
19+import Blaze.ByteString.Builder.Char.Utf8 (fromChar)
20+import Blaze.ByteString.Builder.Word (fromWord8)
1821import Control.Applicative as A
1922import Control.Monad (when)
2023import Data.Aeson.Types (Value(..))
@@ -23,8 +26,9 @@ import Data.Bits ((.|.), shiftL)
2326import Data.ByteString as B
2427import Data.Char (chr)
2528import Data.Map as Map
29+import Data.Monoid (mappend, mempty)
2630import Data.Text as T
27−import Data.Text.Encoding (decodeUtf8, encodeUtf8)
31+import Data.Text.Encoding (decodeUtf8)
2832import Data.Vector as Vector hiding ((++))
2933import Data.Word (Word8)
3034import qualified Data.Attoparsec as A
@@ -36,19 +40,24 @@ json :: Parser Value
3640json = do
3741 c <- skipSpace *> anyChar
3842 case c of
39− '{' -> skipSpace *> object_
40− '[' -> skipSpace *> array_
43+ '{' -> object_
44+ '[' -> array_
4145 _ -> fail "root value is not an object or array"
4246
4347object_ :: Parser Value
4448object_ = do
45− let pair = liftA2 (,) (jstring <* skipSpace) (char8 ':' *> skipSpace *> value)
46− vals <- ((pair <* skipSpace) `sepBy` (char8 ',' *> skipSpace)) <* char8 '}'
49+ skipSpace
50+ let pair = do
51+ a <- jstring <* skipSpace
52+ b <- char ':' *> skipSpace *> value
53+ return (a,b)
54+ vals <- ((pair <* skipSpace) `sepBy` (char ',' *> skipSpace)) <* char '}'
4755 return . Object $ Map.fromList vals
4856
4957array_ :: Parser Value
5058array_ = do
51− vals <- ((value <* skipSpace) `sepBy` (char8 ',' *> skipSpace)) <* char8 ']'
59+ skipSpace
60+ vals <- ((value <* skipSpace) `sepBy` (char ',' *> skipSpace)) <* char ']'
5261 return . Array $ Vector.fromList vals
5362
5463-- | Parse any JSON value. Use 'json' in preference to this function
@@ -59,16 +68,17 @@ value = most <|> (Number <$> double)
5968 most = do
6069 c <- anyChar
6170 case c of
62− '{' -> skipSpace *> object_
63− '[' -> skipSpace *> array_
71+ '{' -> object_
72+ '[' -> array_
6473 '"' -> String <$> jstring_
6574 'f' -> string "alse" *> pure (Bool False)
6675 't' -> string "rue" *> pure (Bool True)
6776 'n' -> string "ull" *> pure Null
68− _ -> A.empty
77+ _ -> fail "not a valid JSON value"
6978
70−doubleQuote :: Word8
79+doubleQuote, backslash :: Word8
7180doubleQuote = 34
81+backslash = 92
7282
7383jstring :: Parser Text
7484jstring = A.word8 doubleQuote *> jstring_
@@ -76,14 +86,13 @@ jstring = A.word8 doubleQuote *> jstring_
7686-- | Parse a string without a leading quote.
7787jstring_ :: Parser Text
7888jstring_ = do
79− let backslash = 92
8089 s <- A.scan False $ \s c -> if s then Just False
8190 else if c == doubleQuote
8291 then Nothing
8392 else Just (c == backslash)
8493 _ <- A.word8 doubleQuote
8594 if backslash `B.elem` s
86− then (decodeUtf8 . B.concat) <$> reparse unescape s
95+ then decodeUtf8 <$> reparse unescape s
8796 else return (decodeUtf8 s)
8897
8998reparse :: Parser a -> ByteString -> Parser a
@@ -92,10 +101,9 @@ reparse p s = case (case parse p s of {Partial k -> k ""; r -> r}) of
92101 Fail _ _ msg -> fail msg
93102 _ -> fail "unexpected failure"
94103
95−unescape :: Parser [ByteString]
96−unescape = Prelude.reverse <$> go [] where
104+unescape :: Parser ByteString
105+unescape = toByteString <$> go mempty where
97106 go acc = do
98− let backslash = 92
99107 h <- A.takeWhile (/=backslash)
100108 let rest = do
101109 start <- A.take 2
@@ -105,21 +113,23 @@ unescape = Prelude.reverse <$> go [] where
105113 Just i -> i
106114 _ -> 255
107115 when (slash /= backslash || escape == 255) $
108− fail "bad escape sequence"
116+ fail "invalid JSON escape sequence"
117+ let continue m = go (acc `mappend` fromByteString h `mappend` m)
118+ {-# INLINE continue #-}
109119 if t /= 117 -- 'u'
110− then go (B.singleton (B.unsafeIndex mapping escape):h:acc)
120+ then continue (fromWord8 (B.unsafeIndex mapping escape))
111121 else do
112122 a <- hexQuad
113123 if a < 0xd800 || a > 0xdfff
114− then go (encodeUtf8 (T.singleton . chr $ a):h:acc)
124+ then continue (fromChar (chr a))
115125 else do
116126 b <- string "\\u" *> hexQuad
117127 if a <= 0xdbff && b >= 0xdc00 && b <= 0xdfff
118128 then let !c = ((a - 0xd800) `shiftL` 10) + (b - 0xdc00) +
119129 0x10000
120− in go (encodeUtf8 (T.singleton (chr c)):h:acc)
130+ in continue (fromChar (chr c))
121131 else fail "invalid UTF-16 surrogates"
122− rest <|> return (h:acc)
132+ rest <|> return (acc `mappend` fromByteString h)
123133 mapping = "\"\\/\n\t\b\r\f"
124134
125135hexQuad :: Parser Int
@@ -130,10 +140,7 @@ hexQuad = do
130140 | w >= 65 && w <= 90 = w - 55
131141 | otherwise = 255
132142 where w = fromIntegral $ B.unsafeIndex s n
133− a = hex 0
134− b = hex 1
135− c = hex 2
136− d = hex 3
143+ a = hex 0; b = hex 1; c = hex 2; d = hex 3
137144 if (a .|. b .|. c .|. d) /= 255
138145 then return $! d .|. (c `shiftL` 4) .|. (b `shiftL` 8) .|. (a `shiftL` 12)
139146 else fail "invalid hex escape"