aeson 1.4.1.0 → 1.4.2.0
raw patch · 19 files changed
+376/−116 lines, 19 filesdep +contravariantdep ~attoparsecdep ~basedep ~base-orphansPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: contravariant
Dependency ranges changed: attoparsec, base, base-orphans, containers, deepseq, dlist, ghc-prim, hashable, nats, primitive, scientific, semigroups, tagged, template-haskell, text, th-abstraction, time, time-locale-compat, transformers, transformers-compat, unordered-containers, vector
API changes (from Hackage documentation)
- Data.Aeson.TH: instance Data.Aeson.TH.LookupField (GHC.Base.Maybe a)
+ Data.Aeson: infixr 8 .=
+ Data.Aeson.QQ.Simple: aesonQQ :: QuasiQuoter
+ Data.Aeson.TH: instance Data.Aeson.TH.LookupField (GHC.Maybe.Maybe a)
+ Data.Aeson.Types: infixr 8 .=
- Data.Aeson: (.:!) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
+ Data.Aeson: (.:!) :: FromJSON a => Object -> Text -> Parser (Maybe a)
- Data.Aeson: (.:) :: (FromJSON a) => Object -> Text -> Parser a
+ Data.Aeson: (.:) :: FromJSON a => Object -> Text -> Parser a
- Data.Aeson: (.:?) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
+ Data.Aeson: (.:?) :: FromJSON a => Object -> Text -> Parser (Maybe a)
- Data.Aeson: FromJSONKeyCoerce :: !(CoerceText a) -> FromJSONKeyFunction a
+ Data.Aeson: FromJSONKeyCoerce :: !CoerceText a -> FromJSONKeyFunction a
- Data.Aeson: FromJSONKeyText :: !(Text -> a) -> FromJSONKeyFunction a
+ Data.Aeson: FromJSONKeyText :: !Text -> a -> FromJSONKeyFunction a
- Data.Aeson: FromJSONKeyTextParser :: !(Text -> Parser a) -> FromJSONKeyFunction a
+ Data.Aeson: FromJSONKeyTextParser :: !Text -> Parser a -> FromJSONKeyFunction a
- Data.Aeson: FromJSONKeyValue :: !(Value -> Parser a) -> FromJSONKeyFunction a
+ Data.Aeson: FromJSONKeyValue :: !Value -> Parser a -> FromJSONKeyFunction a
- Data.Aeson: ToJSONKeyText :: !(a -> Text) -> !(a -> Encoding' Text) -> ToJSONKeyFunction a
+ Data.Aeson: ToJSONKeyText :: !a -> Text -> !a -> Encoding' Text -> ToJSONKeyFunction a
- Data.Aeson: ToJSONKeyValue :: !(a -> Value) -> !(a -> Encoding) -> ToJSONKeyFunction a
+ Data.Aeson: ToJSONKeyValue :: !a -> Value -> !a -> Encoding -> ToJSONKeyFunction a
- Data.Aeson: decode :: (FromJSON a) => ByteString -> Maybe a
+ Data.Aeson: decode :: FromJSON a => ByteString -> Maybe a
- Data.Aeson: decode' :: (FromJSON a) => ByteString -> Maybe a
+ Data.Aeson: decode' :: FromJSON a => ByteString -> Maybe a
- Data.Aeson: decodeFileStrict :: (FromJSON a) => FilePath -> IO (Maybe a)
+ Data.Aeson: decodeFileStrict :: FromJSON a => FilePath -> IO (Maybe a)
- Data.Aeson: decodeFileStrict' :: (FromJSON a) => FilePath -> IO (Maybe a)
+ Data.Aeson: decodeFileStrict' :: FromJSON a => FilePath -> IO (Maybe a)
- Data.Aeson: decodeStrict :: (FromJSON a) => ByteString -> Maybe a
+ Data.Aeson: decodeStrict :: FromJSON a => ByteString -> Maybe a
- Data.Aeson: decodeStrict' :: (FromJSON a) => ByteString -> Maybe a
+ Data.Aeson: decodeStrict' :: FromJSON a => ByteString -> Maybe a
- Data.Aeson: eitherDecode :: (FromJSON a) => ByteString -> Either String a
+ Data.Aeson: eitherDecode :: FromJSON a => ByteString -> Either String a
- Data.Aeson: eitherDecode' :: (FromJSON a) => ByteString -> Either String a
+ Data.Aeson: eitherDecode' :: FromJSON a => ByteString -> Either String a
- Data.Aeson: eitherDecodeFileStrict :: (FromJSON a) => FilePath -> IO (Either String a)
+ Data.Aeson: eitherDecodeFileStrict :: FromJSON a => FilePath -> IO (Either String a)
- Data.Aeson: eitherDecodeFileStrict' :: (FromJSON a) => FilePath -> IO (Either String a)
+ Data.Aeson: eitherDecodeFileStrict' :: FromJSON a => FilePath -> IO (Either String a)
- Data.Aeson: eitherDecodeStrict :: (FromJSON a) => ByteString -> Either String a
+ Data.Aeson: eitherDecodeStrict :: FromJSON a => ByteString -> Either String a
- Data.Aeson: eitherDecodeStrict' :: (FromJSON a) => ByteString -> Either String a
+ Data.Aeson: eitherDecodeStrict' :: FromJSON a => ByteString -> Either String a
- Data.Aeson: encode :: (ToJSON a) => a -> ByteString
+ Data.Aeson: encode :: ToJSON a => a -> ByteString
- Data.Aeson: encodeFile :: (ToJSON a) => FilePath -> a -> IO ()
+ Data.Aeson: encodeFile :: ToJSON a => FilePath -> a -> IO ()
- Data.Aeson: fromJSON :: (FromJSON a) => Value -> Result a
+ Data.Aeson: fromJSON :: FromJSON a => Value -> Result a
- Data.Aeson.Encoding.Internal: Value :: (Encoding' Series) -> Series
+ Data.Aeson.Encoding.Internal: Value :: Encoding' Series -> Series
- Data.Aeson.Internal: ifromJSON :: (FromJSON a) => Value -> IResult a
+ Data.Aeson.Internal: ifromJSON :: FromJSON a => Value -> IResult a
- Data.Aeson.Types: (.:!) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
+ Data.Aeson.Types: (.:!) :: FromJSON a => Object -> Text -> Parser (Maybe a)
- Data.Aeson.Types: (.:) :: (FromJSON a) => Object -> Text -> Parser a
+ Data.Aeson.Types: (.:) :: FromJSON a => Object -> Text -> Parser a
- Data.Aeson.Types: (.:?) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
+ Data.Aeson.Types: (.:?) :: FromJSON a => Object -> Text -> Parser (Maybe a)
- Data.Aeson.Types: FromJSONKeyCoerce :: !(CoerceText a) -> FromJSONKeyFunction a
+ Data.Aeson.Types: FromJSONKeyCoerce :: !CoerceText a -> FromJSONKeyFunction a
- Data.Aeson.Types: FromJSONKeyText :: !(Text -> a) -> FromJSONKeyFunction a
+ Data.Aeson.Types: FromJSONKeyText :: !Text -> a -> FromJSONKeyFunction a
- Data.Aeson.Types: FromJSONKeyTextParser :: !(Text -> Parser a) -> FromJSONKeyFunction a
+ Data.Aeson.Types: FromJSONKeyTextParser :: !Text -> Parser a -> FromJSONKeyFunction a
- Data.Aeson.Types: FromJSONKeyValue :: !(Value -> Parser a) -> FromJSONKeyFunction a
+ Data.Aeson.Types: FromJSONKeyValue :: !Value -> Parser a -> FromJSONKeyFunction a
- Data.Aeson.Types: ToJSONKeyText :: !(a -> Text) -> !(a -> Encoding' Text) -> ToJSONKeyFunction a
+ Data.Aeson.Types: ToJSONKeyText :: !a -> Text -> !a -> Encoding' Text -> ToJSONKeyFunction a
- Data.Aeson.Types: ToJSONKeyValue :: !(a -> Value) -> !(a -> Encoding) -> ToJSONKeyFunction a
+ Data.Aeson.Types: ToJSONKeyValue :: !a -> Value -> !a -> Encoding -> ToJSONKeyFunction a
- Data.Aeson.Types: fromJSON :: (FromJSON a) => Value -> Result a
+ Data.Aeson.Types: fromJSON :: FromJSON a => Value -> Result a
- Data.Aeson.Types: parseField :: (FromJSON a) => Object -> Text -> Parser a
+ Data.Aeson.Types: parseField :: FromJSON a => Object -> Text -> Parser a
- Data.Aeson.Types: parseFieldMaybe :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
+ Data.Aeson.Types: parseFieldMaybe :: FromJSON a => Object -> Text -> Parser (Maybe a)
- Data.Aeson.Types: parseFieldMaybe' :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
+ Data.Aeson.Types: parseFieldMaybe' :: FromJSON a => Object -> Text -> Parser (Maybe a)
Files
- Data/Aeson/Parser/Internal.hs +52/−3
- Data/Aeson/QQ/Simple.hs +25/−0
- Data/Aeson/Types/ToJSON.hs +11/−0
- aeson.cabal +41/−24
- benchmarks/Compare.hs +8/−2
- benchmarks/Compare/JsonBench.hs +8/−0
- benchmarks/Issue673.hs +164/−0
- benchmarks/aeson-benchmarks.cabal +23/−2
- changelog.md +10/−3
- stack-bench.yaml +1/−3
- stack-ffi-unescape.yaml +2/−2
- stack-lts10.yaml +0/−9
- stack-lts12.yaml +10/−0
- stack-lts6.yaml +0/−23
- stack-lts7.yaml +0/−17
- stack-lts8.yaml +0/−14
- stack-lts9.yaml +0/−12
- stack-nightly.yaml +1/−1
- tests/UnitTests.hs +20/−1
Data/Aeson/Parser/Internal.hs view
@@ -328,12 +328,11 @@ decimal0 :: Parser Integer decimal0 = do- let step a w = a * 10 + fromIntegral (w - zero)- zero = 48+ let zero = 48 digits <- A.takeWhile1 isDigit_w8 if B.length digits > 1 && B.unsafeHead digits == zero then fail "leading zero"- else return (B.foldl' step 0 digits)+ else return (bsToInteger digits) -- | Parse a JSON number. scientific :: Parser Scientific@@ -366,3 +365,53 @@ fmap (Sci.scientific signedCoeff . (e +)) (signed decimal)) <|> return (Sci.scientific signedCoeff e) {-# INLINE scientific #-}++------------------ Copy-pasted and adapted from base ------------------------++bsToInteger :: B.ByteString -> Integer+bsToInteger bs+ | l > 40 = valInteger 10 l [ fromIntegral (w - 48) | w <- B.unpack bs ]+ | otherwise = bsToIntegerSimple bs+ where+ l = B.length bs++bsToIntegerSimple :: B.ByteString -> Integer+bsToIntegerSimple = B.foldl' step 0 where+ step a b = a * 10 + fromIntegral (b - 48) -- 48 = '0'++-- A sub-quadratic algorithm for Integer. Pairs of adjacent radix b+-- digits are combined into a single radix b^2 digit. This process is+-- repeated until we are left with a single digit. This algorithm+-- performs well only on large inputs, so we use the simple algorithm+-- for smaller inputs.+valInteger :: Integer -> Int -> [Integer] -> Integer+valInteger = go+ where+ go :: Integer -> Int -> [Integer] -> Integer+ go _ _ [] = 0+ go _ _ [d] = d+ go b l ds+ | l > 40 = b' `seq` go b' l' (combine b ds')+ | otherwise = valSimple b ds+ where+ -- ensure that we have an even number of digits+ -- before we call combine:+ ds' = if even l then ds else 0 : ds+ b' = b * b+ l' = (l + 1) `quot` 2++ combine b (d1 : d2 : ds) = d `seq` (d : combine b ds)+ where+ d = d1 * b + d2+ combine _ [] = []+ combine _ [_] = errorWithoutStackTrace "this should not happen"++-- The following algorithm is only linear for types whose Num operations+-- are in constant time.+valSimple :: Integer -> [Integer] -> Integer+valSimple base = go 0+ where+ go r [] = r+ go r (d : ds) = r' `seq` go r' ds+ where+ r' = r * base + fromIntegral d
+ Data/Aeson/QQ/Simple.hs view
@@ -0,0 +1,25 @@+-- | Like "Data.Aeson.QQ" but without interpolation.+module Data.Aeson.QQ.Simple (aesonQQ) where++import Data.Aeson+import qualified Data.Text as T+import qualified Data.Text.Encoding as TE+import Language.Haskell.TH+import Language.Haskell.TH.Quote+import Language.Haskell.TH.Syntax (Lift (..))+import Prelude ()+import Prelude.Compat++aesonQQ :: QuasiQuoter+aesonQQ = QuasiQuoter+ { quoteExp = aesonExp+ , quotePat = const $ error "No quotePat defined for jsonQQ"+ , quoteType = const $ error "No quoteType defined for jsonQQ"+ , quoteDec = const $ error "No quoteDec defined for jsonQQ"+ }++aesonExp :: String -> ExpQ+aesonExp txt =+ case eitherDecodeStrict $ TE.encodeUtf8 $ T.pack txt of+ Left err -> error $ "Error in aesonExp: " ++ show err+ Right val -> lift (val :: Value)
Data/Aeson/Types/ToJSON.hs view
@@ -74,6 +74,7 @@ import Data.Fixed (Fixed, HasResolution) import Data.Foldable (toList) import Data.Functor.Compose (Compose(..))+import Data.Functor.Contravariant (Contravariant (..)) import Data.Functor.Identity (Identity(..)) import Data.Functor.Product (Product(..)) import Data.Functor.Sum (Sum(..))@@ -346,6 +347,13 @@ name .= value = (name, toJSON value) {-# INLINE (.=) #-} +-- | Constructs a singleton 'H.HashMap'. For calling functions that+-- demand an 'Object' for constructing objects. To be used in+-- conjunction with 'mconcat'. Prefer to use 'object' where possible.+instance KeyValue Object where+ name .= value = H.singleton name (toJSON value)+ {-# INLINE (.=) #-}+ ------------------------------------------------------------------------------- -- Classes and types for map keys -------------------------------------------------------------------------------@@ -497,6 +505,9 @@ . lazyToStrictByteString . E.encodingToLazyByteString . e++instance Contravariant ToJSONKeyFunction where+ contramap = contramapToJSONKeyFunction -- | Contravariant map, as 'ToJSONKeyFunction' is a contravariant functor. contramapToJSONKeyFunction :: (b -> a) -> ToJSONKeyFunction a -> ToJSONKeyFunction b
aeson.cabal view
@@ -1,5 +1,5 @@ name: aeson-version: 1.4.1.0+version: 1.4.2.0 license: BSD3 license-file: LICENSE category: Text, Web, JSON@@ -75,6 +75,7 @@ Data.Aeson.Text Data.Aeson.Types Data.Aeson.TH+ Data.Aeson.QQ.Simple Data.Aeson.Encoding.Internal Data.Aeson.Internal@@ -99,45 +100,61 @@ Data.Attoparsec.Time Data.Attoparsec.Time.Internal + -- GHC bundled libs build-depends:- attoparsec >= 0.13.0.1,- base >= 4.5 && < 5,- base-compat >= 0.9.1 && < 0.11,- containers >= 0.4.2,- deepseq >= 1.3 && < 1.5,- dlist >= 0.6,- ghc-prim >= 0.2,- hashable >= 1.1.2.0,- primitive >= 0.6.1,- scientific >= 0.3.4.7 && < 0.4,- tagged >=0.8.3 && <0.9,- template-haskell >= 2.7,- text >= 1.2.3,- th-abstraction >= 0.2.2 && < 0.3,- time >= 1.1.1.4,- time-locale-compat >= 0.1.1 && < 0.2,- unordered-containers >= 0.2.5.0 && < 0.3,- uuid-types >= 1.0.3 && <1.1,- vector >= 0.8+ base >= 4.5.0.0 && < 5,+ containers >= 0.4.2.1 && < 0.7,+ deepseq >= 1.3.0.0 && < 1.5,+ ghc-prim >= 0.2 && < 0.6,+ template-haskell >= 2.7.0.0 && < 2.15,+ text >= 1.2.3.0 && < 1.3,+ time >= 1.4 && < 1.9 + -- Compat+ build-depends:+ base-compat >= 0.9.1 && < 0.11+ if flag(bytestring-builder) build-depends: bytestring >= 0.9.2 && < 0.10.4, bytestring-builder >= 0.10.4 && < 1 else build-depends: bytestring >= 0.10.4 && < 0.11 + if !impl(ghc >= 8.6)+ build-depends:+ contravariant >=1.4.1 && <1.6+ if !impl(ghc >= 8.0) -- `Data.Semigroup` and `Control.Monad.Fail` and `Control.Monad.IO.Class` are available in base only since GHC 8.0 / base 4.9 build-depends:- semigroups >= 0.18.2 && < 0.19,- transformers >= 0.2.2.0,- transformers-compat >= 0.3,+ semigroups >= 0.18.5 && < 0.19,+ transformers >= 0.3.0.0 && < 0.6,+ transformers-compat >= 0.6.2 && < 0.7, fail == 4.9.* if !impl(ghc >= 7.10) -- `Numeric.Natural` is available in base only since GHC 7.10 / base 4.8- build-depends: nats >= 1 && < 1.2,+ build-depends: nats >= 1.1.1 && < 1.2, void >= 0.7.2 && < 0.8++ -- cannot use latest version+ build-depends:+ unordered-containers >= 0.2.8.0 && < 0.3,++ -- not in LTS-12.10+ tagged >= 0.8.5 && < 0.9,+ primitive >= 0.6.3.0 && < 0.7++ -- Other dependencies+ build-depends:+ attoparsec >= 0.13.2.2 && < 0.14,+ dlist >= 0.8.0.4 && < 0.9,+ hashable >= 1.2.7.0 && < 1.3,+ scientific >= 0.3.6.2 && < 0.4,+ th-abstraction >= 0.2.8.0 && < 0.3,+ time-locale-compat >= 0.1.1.5 && < 0.2,+ uuid-types >= 1.0.3 && < 1.1,+ vector >= 0.12.0.1 && < 0.13 ghc-options: -Wall
benchmarks/Compare.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -6,16 +7,19 @@ import Prelude.Compat import Compare.BufferBuilder ()-import Compare.JsonBuilder () import Criterion.Main import Data.BufferBuilder.Json-import Data.Json.Builder import Twitter import Twitter.Manual () import Typed.Common import qualified Data.Aeson as Aeson import qualified Compare.JsonBench as JsonBench +#ifdef MIN_VERSION_json_builder+import Data.Json.Builder+import Compare.JsonBuilder ()+#endif+ main :: IO () main = defaultMain [@@ -23,7 +27,9 @@ bgroup "twitter" [ bench "aeson" $ nf Aeson.encode twtr , bench "buffer-builder" $ nf encodeJson twtr+#ifdef MIN_VERSION_json_builder , bench "json-builder" $ nf toJsonLBS twtr+#endif ] , JsonBench.benchmarks ]
benchmarks/Compare/JsonBench.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE NoImplicitPrelude #-}@@ -21,7 +22,10 @@ import Typed.Common (load) import qualified Data.Aeson as Aeson import qualified Data.BufferBuilder.Json as Json++#ifdef MIN_VERSION_json_builder import qualified Data.Json.Builder as JB+#endif data EyeColor = Green | Blue | Brown deriving (Eq, Show)@@ -268,6 +272,7 @@ <> "greeting"# Json..=# uGreeting <> "favoriteFruit"# Json..=# uFavouriteFruit +#ifdef MIN_VERSION_json_builder ---- json-builder instances ---- instance JB.Value EyeColor where@@ -319,6 +324,7 @@ <> t "friends" `JB.row` uFriends <> t "greeting" `JB.row` uGreeting <> t "favoriteFruit" `JB.row` uFavouriteFruit+#endif benchmarks :: Benchmark benchmarks = env (load "json-data/buffer-builder.json") $@@ -326,5 +332,7 @@ bgroup "json-bench" [ bench "aeson" $ nf Aeson.encode parsedUserList , bench "buffer-builder" $ nf Json.encodeJson parsedUserList+#ifdef MIN_VERSION_json_builder , bench "json-builder" $ nf JB.toJsonLBS parsedUserList+#endif ]
+ benchmarks/Issue673.hs view
@@ -0,0 +1,164 @@+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE OverloadedStrings #-}++module Main (+ main,+ input17,+ input32,+ input64,+ input128,+ input256,+ input2048,+ input4096,+ input8192,+ input16384,+ ) where++import Criterion.Main+import Prelude.Compat+import Data.Int (Int64)+import Data.Scientific (Scientific)+import Data.Aeson.Parser (scientific)++import qualified Data.Attoparsec.ByteString.Lazy as AttoL+import qualified Data.Attoparsec.ByteString.Char8 as Atto8+import qualified Data.Aeson as A+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as LBS+import qualified Data.ByteString.Lazy.Char8 as LBS8++decodeInt :: LBS.ByteString -> Maybe Int+decodeInt = A.decode++decodeString :: LBS.ByteString -> Maybe String+decodeString = A.decode++decodeScientific :: LBS.ByteString -> Maybe Scientific+decodeScientific = A.decode++decodeViaRead :: LBS.ByteString -> Integer+decodeViaRead = read . LBS8.unpack++decodeAtto :: LBS.ByteString -> Maybe Scientific+decodeAtto+ = parseOnly (scientific <* AttoL.endOfInput)+ where+ parseOnly p lbs = case AttoL.parse p lbs of+ AttoL.Done _ r -> Just r+ AttoL.Fail {} -> Nothing++decodeAtto8 :: LBS.ByteString -> Maybe Scientific+decodeAtto8+ = parseOnly (Atto8.scientific <* AttoL.endOfInput)+ where+ parseOnly p lbs = case AttoL.parse p lbs of+ AttoL.Done _ r -> Just r+ AttoL.Fail {} -> Nothing++generate :: Int64 -> LBS.ByteString+generate n = LBS8.replicate n '1'++input17 :: LBS.ByteString+input17 = generate 17++input32 :: LBS.ByteString+input32 = generate 32++input64 :: LBS.ByteString+input64 = generate 64++input128 :: LBS.ByteString+input128 = generate 128++input256 :: LBS.ByteString+input256 = generate 256++input2048 :: LBS.ByteString+input2048 = generate 2048++input4096 :: LBS.ByteString+input4096 = generate 4096++input8192 :: LBS.ByteString+input8192 = generate 8192++input16384 :: LBS.ByteString+input16384 = generate 16384+++main :: IO ()+main = defaultMain+ -- works on 64bit+ [ benchPair "17" input17+ -- , benchPair "32" input32+ -- , benchPair "64" input64+ -- , benchPair "128" input128+ -- , benchPair "256" input256+ , benchPair "2048" input2048+ , benchPair "4096" input4096+ , benchPair "8192" input8192+ , benchPair "16384" input16384+ ]+ where+ benchPair name input = bgroup name+ [ bench "Int" $ whnf decodeInt input+ , bench "Simple" $ whnf bsToIntegerSimple (LBS.toStrict input)+ , bench "Optim" $ whnf bsToInteger (LBS.toStrict input)+ , bench "Read" $ whnf decodeViaRead input+ , bench "Scientific" $ whnf decodeScientific input+ , bench "parserA" $ whnf decodeAtto input+ , bench "parserS" $ whnf decodeAtto8 input+ , bench "String" $ whnf decodeString $ "\"" <> input <> "\""+ ]++-------------------------------------------------------------------------------+-- better fromInteger+-------------------------------------------------------------------------------++bsToInteger :: BS.ByteString -> Integer+bsToInteger bs+ | l > 40 = valInteger 10 l [ fromIntegral (w - 48) | w <- BS.unpack bs ]+ | otherwise = bsToIntegerSimple bs+ where+ l = BS.length bs++bsToIntegerSimple :: BS.ByteString -> Integer+bsToIntegerSimple = BS.foldl' step 0 where+ step a b = a * 10 + fromIntegral (b - 48) -- 48 = '0'++-- A sub-quadratic algorithm for Integer. Pairs of adjacent radix b+-- digits are combined into a single radix b^2 digit. This process is+-- repeated until we are left with a single digit. This algorithm+-- performs well only on large inputs, so we use the simple algorithm+-- for smaller inputs.+valInteger :: Integer -> Int -> [Integer] -> Integer+valInteger = go+ where+ go :: Integer -> Int -> [Integer] -> Integer+ go _ _ [] = 0+ go _ _ [d] = d+ go b l ds+ | l > 40 = b' `seq` go b' l' (combine b ds')+ | otherwise = valSimple b ds+ where+ -- ensure that we have an even number of digits+ -- before we call combine:+ ds' = if even l then ds else 0 : ds+ b' = b * b+ l' = (l + 1) `quot` 2++ combine b (d1 : d2 : ds) = d `seq` (d : combine b ds)+ where+ d = d1 * b + d2+ combine _ [] = []+ combine _ [_] = errorWithoutStackTrace "this should not happen"++-- The following algorithm is only linear for types whose Num operations+-- are in constant time.+valSimple :: Integer -> [Integer] -> Integer+valSimple base = go 0+ where+ go r [] = r+ go r (d : ds) = r' `seq` go r' ds+ where+ r' = r * base + fromIntegral d
benchmarks/aeson-benchmarks.cabal view
@@ -63,6 +63,10 @@ -- `Data.Semigroup` is available in base only since GHC 8.0 / base 4.9 build-depends: semigroups >= 0.18.2 && < 0.19 + if !impl(ghc >= 8.6)+ build-depends:+ contravariant >=1.4.1 && <1.6+ include-dirs: ../include ghc-options: -O2 -Wall@@ -155,7 +159,6 @@ other-modules: Compare.BufferBuilder Compare.JsonBench- Compare.JsonBuilder Twitter Twitter.Manual Typed.Common@@ -168,9 +171,14 @@ criterion >= 1.0, deepseq, ghc-prim,- json-builder, text + if impl(ghc < 8.4)+ other-modules:+ Compare.JsonBuilder+ build-depends:+ json-builder+ executable aeson-benchmark-micro default-language: Haskell2010 main-is: Micro.hs@@ -318,3 +326,16 @@ text, unordered-containers, vector++executable aeson-issue-673+ default-language: Haskell2010+ main-is: Issue673.hs+ ghc-options: -Wall -O1 -rtsopts+ build-depends:+ aeson-benchmarks,+ attoparsec,+ base,+ bytestring,+ scientific,+ base-compat,+ criterion >= 1.0
changelog.md view
@@ -1,7 +1,14 @@ For the latest version of this document, please see [https://github.com/bos/aeson/blob/master/changelog.md](https://github.com/bos/aeson/blob/master/changelog.md). -#### 1.4.1.0+### 1.4.2.0 +* Add `Data.Aeson.QQ.Simple` which is a simpler version of the `aeson-qq` package, it does not support interpolation, thanks to Oleg Grenrus.+* Add `Contravariant ToJSONKeyFunction` instance, thanks to Oleg Grenrus.+* Add `KeyValue Object` instance, thanks to Robert Hensing+* Improved performance when parsing large numbers, thanks to Oleg Grenrus.++### 1.4.1.0+ * Optimizations of generics, thanks to Rémy Oudompheng, here are some numbers for GHC 8.4: * Compilation time: G/BigProduct.hs is 25% faster, G/BigRecord.hs is 2x faster. * Runtime performance: BigRecord/toJSON/generic and BigProduct/encode/generic are more than 2x faster.@@ -10,7 +17,7 @@ * Fixes handling of `UTCTime` wrt. leap seconds , thanks to Adam Schønemann * Warning and documentation fixes thanks to tom-bop, Gabor Greif, Ian Jeffries, and Mateusz Curyło. -### 1.4.0.0+## 1.4.0.0 This release introduces bounds on the size of `Scientific` numbers when they are converted to other arbitrary precision types that do not represent them efficiently in memory. @@ -29,7 +36,7 @@ Finally, encoding integral values with large exponents now uses scientific notation, this saves space for large numbers. -### 1.3.1.1+#### 1.3.1.1 * Catch 0 denominators when parsing Ratio
stack-bench.yaml view
@@ -1,4 +1,4 @@-resolver: nightly-2018-03-12+resolver: lts-12.10 # We use aeson in the snapshot to # - avoid recompilation of criterion # - compare against it@@ -9,5 +9,3 @@ work-dir: .stack-work-bench packages: - benchmarks-extra-deps:-- aeson-1.3.1.0
stack-ffi-unescape.yaml view
@@ -1,4 +1,4 @@-resolver: lts-10.0+resolver: lts-12.10 packages: - '.' flags:@@ -6,4 +6,4 @@ fast: true cffi: true extra-deps:-- text-1.2.3.0+- hashable-time-0.2.0.1
− stack-lts10.yaml
@@ -1,9 +0,0 @@-resolver: lts-10.0-packages:-- '.'-flags:- aeson:- fast: true- cffi: false- attoparsec-iso8601:- fast: true
+ stack-lts12.yaml view
@@ -0,0 +1,10 @@+resolver: lts-12.10+packages:+- '.'+- attoparsec-iso8601+flags:+ aeson:+ fast: true+ cffi: true+extra-deps:+- hashable-time-0.2.0.1
− stack-lts6.yaml
@@ -1,23 +0,0 @@-resolver: lts-6.35-packages:-- '.'-- attoparsec-iso8601-extra-deps:-- semigroups-0.18.2-- tagged-0.8.5-- transformers-compat-0.5.1.4-- QuickCheck-2.10.0.1-- base-compat-0.9.3-- hashable-1.2.6.1-- integer-logarithms-1-- quickcheck-instances-0.3.16-- th-abstraction-0.2.2.0-- text-1.2.3.0-flags:-flags:- aeson:- fast: true- attoparsec-iso8601:- fast: true- semigroups:- bytestring-builder: false
− stack-lts7.yaml
@@ -1,17 +0,0 @@-resolver: lts-7.24-packages:-- '.'-- attoparsec-iso8601-extra-deps:-- QuickCheck-2.10.0.1-- base-compat-0.9.3-- hashable-1.2.6.1-- integer-logarithms-1-- quickcheck-instances-0.3.16-- th-abstraction-0.2.2.0-- text-1.2.3.0-flags:- aeson:- fast: true- attoparsec-iso8601:- fast: true
− stack-lts8.yaml
@@ -1,14 +0,0 @@-resolver: lts-8.24-packages:-- '.'-- attoparsec-iso8601-extra-deps:-- QuickCheck-2.10.0.1-- base-compat-0.9.3-- quickcheck-instances-0.3.16-- th-abstraction-0.2.2.0-flags:- aeson:- fast: true- attoparsec-iso8601:- fast: true
− stack-lts9.yaml
@@ -1,12 +0,0 @@-resolver: lts-9.9-packages:-- '.'-- attoparsec-iso8601-extra-deps:-- quickcheck-instances-0.3.16-- QuickCheck-2.10.0.1-flags:- aeson:- fast: true- attoparsec-iso8601:- fast: true
stack-nightly.yaml view
@@ -1,4 +1,4 @@-resolver: nightly-2018-03-07+resolver: nightly-2018-09-26 packages: - '.' - attoparsec-iso8601
tests/UnitTests.hs view
@@ -5,6 +5,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE QuasiQuotes #-} -- For Data.Aeson.Types.camelTo {-# OPTIONS_GHC -fno-warn-deprecations #-}@@ -25,9 +26,10 @@ import Control.Monad (forM, forM_) import Data.Aeson ((.=), (.:), (.:?), (.:!), FromJSON(..), FromJSONKeyFunction(..), FromJSONKey(..), ToJSON1(..), decode, eitherDecode, encode, fromJSON, genericParseJSON, genericToEncoding, genericToJSON, object, withObject, withEmbeddedJSON) import Data.Aeson.Internal (JSONPathElement(..), formatError)+import Data.Aeson.QQ.Simple (aesonQQ) import Data.Aeson.TH (deriveJSON, deriveToJSON, deriveToJSON1) import Data.Aeson.Text (encodeToTextBuilder)-import Data.Aeson.Types (Options(..), Result(Success), ToJSON(..), Value(Null), camelTo, camelTo2, defaultOptions, omitNothingFields, parse)+import Data.Aeson.Types (Options(..), Result(Success), ToJSON(..), Value(Null, Object), camelTo, camelTo2, defaultOptions, omitNothingFields, parse) import Data.Char (toUpper) import Data.Either.Compat (isLeft, isRight) import Data.Hashable (hash)@@ -93,6 +95,7 @@ , testGroup ".:, .:?, .:!" $ fmap (testCase "-") dotColonMark , testGroup "JSONPath" $ fmap (testCase "-") jsonPath , testGroup "Hashable laws" $ fmap (testCase "-") hashableLaws+ , testGroup "Object construction" $ fmap (testCase "-") objectConstruction , testGroup "Issue #351" $ fmap (testCase "-") issue351 , testGroup "Nullary constructors" $ fmap (testCase "-") nullaryConstructors , testGroup "FromJSONKey" $ fmap (testCase "-") fromJSONKeyAssertions@@ -107,6 +110,10 @@ , testCase "Big integer decoding" bigIntegerDecoding , testCase "Big natural decading" bigNaturalDecoding , testCase "Big integer key decoding" bigIntegerKeyDecoding+ , testGroup "QQ.Simple"+ [ testCase "example" $+ assertEqual "" (object ["foo" .= True]) [aesonQQ| {"foo": true } |]+ ] ] roundTripCamel :: String -> Assertion@@ -301,6 +308,18 @@ where a = object ["223" .= False, "807882556" .= True] b = object ["807882556" .= True, "223" .= False]++------------------------------------------------------------------------------+-- Check that an alternative way to construct objects works+------------------------------------------------------------------------------++objectConstruction :: [Assertion]+objectConstruction = [+ assertEqual "Equal objects constructed differently" recommended notRecommended+ ]+ where+ recommended = object ["foo" .= True, "bar" .= (-1 :: Int)]+ notRecommended = Object (mconcat ["foo" .= True, "bar" .= (-1 :: Int)]) ------------------------------------------------------------------------------- -- ToJSONKey