buffer-builder-aeson (empty) → 0.1.0.1
raw patch · 6 files changed
+250/−0 lines, 6 filesdep +HUnitdep +QuickCheckdep +aesonsetup-changed
Dependencies added: HUnit, QuickCheck, aeson, attoparsec, base, buffer-builder, buffer-builder-aeson, bytestring, criterion, deepseq, hashable, integer-gmp, scientific, tasty, tasty-hunit, tasty-quickcheck, tasty-th, text, unordered-containers, vector
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- bench/Bench.hs +49/−0
- buffer-builder-aeson.cabal +78/−0
- src/Data/BufferBuilder/Aeson.hs +39/−0
- test/Main.hs +52/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, IMVU, Chad Austin, Andy Friesen++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Chad Austin nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ bench/Bench.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE OverloadedStrings, RecordWildCards #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE BangPatterns #-}++module Main where++import Control.DeepSeq (force)+import Criterion+import Criterion.Main+import qualified Data.Aeson as Aeson+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BSL+import qualified Data.BufferBuilder.Json as Json+import qualified Data.BufferBuilder.Aeson ()+import qualified Data.Text as Text+import qualified Data.Vector as Vector+import qualified Data.Vector.Unboxed as UnboxedVector++assumeSuccess :: Either a b -> b+assumeSuccess (Right r) = r+assumeSuccess _ = error "assumeSuccess"++main :: IO ()+main = do+ content <- BS.readFile "test.json"+ let lazyContent = force $ BSL.fromChunks [content]++ let parsedUserList :: [Aeson.Value]+ Just parsedUserList = Aeson.decode lazyContent++ let compareBench name !value =+ bgroup name+ [ bench "bufferbuilder" $ nf Json.encodeJson value+ , bench "aeson" $ nf Aeson.encode value+ ]++ defaultMain+ [ compareBench "list bool" $ Aeson.Array $ Vector.fromList $ fmap Aeson.Bool $ replicate 65536 False+ , compareBench "list null" $ Aeson.Array $ Vector.replicate 65536 Aeson.Null+ , compareBench "list empty object" $ Aeson.Array $ Vector.replicate 65536 $ Aeson.object []+ , compareBench "list empty array" $ Aeson.Array $ Vector.replicate 65536 $ Aeson.Array Vector.empty+ , compareBench "list string" $ Aeson.Array $ Vector.fromList $ fmap (Aeson.String . Text.pack . show) ([0..65535] :: [Int])+ , compareBench "list int" $ Aeson.Array $ Vector.fromList $ fmap (Aeson.Number . fromIntegral) ([0..65535] :: [Int])+ , compareBench "hash string" $ Aeson.object $ fmap (\e -> (Text.pack $ show e, Aeson.Null)) ([0..65535] :: [Int])++ , compareBench "list record" parsedUserList+ , bench "intvector" $ nf Json.encodeJson (UnboxedVector.fromList $! [0..65535] :: UnboxedVector.Vector Int)+ ]
+ buffer-builder-aeson.cabal view
@@ -0,0 +1,78 @@+-- Initial buffer-builder-aeson.cabal generated by cabal init. For further+-- documentation, see http://haskell.org/cabal/users-guide/++name: buffer-builder-aeson+version: 0.1.0.1+synopsis: Serialize Aeson values with Data.BufferBuilder+-- description:+license: MIT+license-file: LICENSE+author: Andy Friesen+maintainer: andy.friesen@gmail.com+-- copyright:+category: Data+build-type: Simple+-- extra-source-files:+cabal-version: >=1.10++library+ exposed-modules: Data.BufferBuilder.Aeson+ hs-source-dirs: src+ ghc-options: -O2 -Wall+ -- ghc-options: -ddump-ds -ddump-simpl -ddump-stg -ddump-opt-cmm -ddump-asm -ddump-to-file++ -- other-modules:+ -- other-extensions:+ build-depends: base == 4.*+ , aeson+ , integer-gmp+ , buffer-builder >=0.2.0.0+ , unordered-containers+ , vector+ , scientific+ , bytestring+ hs-source-dirs:+ default-language: Haskell2010++test-suite tests+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs: test+ default-language: Haskell2010+ ghc-options: -O2 -Wall++ build-depends: base ==4.*+ , buffer-builder-aeson+ , buffer-builder+ , text+ , tasty+ , tasty-hunit+ , tasty-quickcheck+ , tasty-th+ , QuickCheck+ , unordered-containers+ , vector+ , scientific+ , bytestring+ , HUnit+ , text+ , hashable+ , attoparsec+ , aeson++benchmark json-bench+ type: exitcode-stdio-1.0+ main-is: Bench.hs+ hs-source-dirs: bench+ default-language: Haskell2010+ ghc-options: -O2 -Wall+ -- ghc-options: -ddump-ds -ddump-simpl -ddump-stg -ddump-opt-cmm -ddump-asm -ddump-to-file+ build-depends: base+ , buffer-builder+ , buffer-builder-aeson+ , aeson+ , bytestring+ , text+ , deepseq+ , criterion+ , vector
+ src/Data/BufferBuilder/Aeson.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE MagicHash #-}+{-# OPTIONS_GHC -fno-warn-orphans #-} ++module Data.BufferBuilder.Aeson () where++import GHC.Base+import GHC.Integer.GMP.Internals+import Data.Aeson (Value (..))+import Data.BufferBuilder.Json (ToJson (..), nullValue, unsafeAppendBS, unsafeAppendUtf8Builder)+import qualified Data.BufferBuilder.Json as Json+import qualified Data.BufferBuilder.Utf8 as Utf8Builder+import qualified Data.ByteString.Lazy as BSL+import qualified Data.ByteString.Lazy.Builder as BB+import qualified Data.ByteString.Builder.Scientific as BB+import qualified Data.Scientific as Scientific++-- TODO: this doesn't need to convert the bytestring to strict before appending it+-- there is an appendBSL+slowNumber :: Scientific.Scientific -> Json.Value+slowNumber n = unsafeAppendBS+ $ BSL.toStrict+ $ BB.toLazyByteString+ $ BB.formatScientificBuilder BB.Fixed Nothing n++instance ToJson Value where+ {-# INLINE toJson #-}+ toJson (Object o) = toJson o+ toJson (Array a) = toJson a+ toJson (String s) = toJson s+ toJson (Number n) = case Scientific.coefficient n of+ (S# smallcoeff) -> case Scientific.base10Exponent n of+ 0 -> toJson (I# smallcoeff)+ exp' -> unsafeAppendUtf8Builder $ do+ Utf8Builder.appendDecimalSignedInt (I# smallcoeff)+ Utf8Builder.appendChar7 'e'+ Utf8Builder.appendDecimalSignedInt exp'+ _ -> slowNumber n+ toJson (Bool b) = toJson b+ toJson Null = nullValue
+ test/Main.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE MagicHash, OverloadedStrings, TemplateHaskell #-}++module Main where++import Test.Tasty+import Test.Tasty.TH+import Test.Tasty.HUnit+import Test.Tasty.QuickCheck+import Data.String (IsString (..))+import Data.BufferBuilder.Json+import Data.BufferBuilder.Aeson ()+import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.Attoparsec.ByteString as Atto+import qualified Data.Aeson.Parser as JsonParse+import qualified Data.Aeson as Aeson+import AesonQuickCheck ()+import Debug.Trace++ae :: String -> ByteString -> IO ()+ae expected actual = assertEqual expected (fromString expected) actual++decodeJsonFragment :: Aeson.FromJSON j => BS.ByteString -> Maybe j+decodeJsonFragment str = case parsed of+ Right r -> case Aeson.fromJSON r of+ Aeson.Success a -> Just a+ _ -> Nothing+ _ -> Nothing+ where+ parsed = Atto.parseOnly JsonParse.value' str++case_serialize_simple_things :: IO ()+case_serialize_simple_things = do+ ae "false" (encodeJson $ Aeson.Bool False)+ ae "true" (encodeJson $ Aeson.Bool True)+ ae "null" (encodeJson $ Aeson.Null)+ ae "9" (encodeJson $ Aeson.Number 9)+ ae "-9" (encodeJson $ Aeson.Number (-9))++prop_matches_aeson :: Aeson.Value -> Bool+prop_matches_aeson value =+ let encoded = encodeJson value+ decoded = decodeJsonFragment encoded+ in if decoded == Just value+ then True+ else trace (show (value, encoded, decoded)) False++tests :: TestTree+tests = $(testGroupGenerator)++main :: IO ()+main = defaultMain tests