Skip to content

Commit 2cc159f

Browse files
committed
Add convenience functions for creating known-length objects
Also, add encoding benchmarks to the benchmark suite. These benchmarks show that json-syntax is currently outperformed by aeson when encoding JSON values.
1 parent 1066a8e commit 2cc159f

3 files changed

Lines changed: 101 additions & 27 deletions

File tree

bench/Main.hs

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,48 @@
11
{-# LANGUAGE BangPatterns #-}
2+
{-# LANGUAGE ScopedTypeVariables #-}
23
{-# LANGUAGE TypeApplications #-}
34

45
import Gauge.Main (defaultMain,bgroup,bench,whnf)
56

67
import Twitter100 (encodedTwitter100,byteStringTwitter100)
78

89
import qualified Data.Bytes as Bytes
10+
import qualified Data.Bytes.Builder as BLDR
11+
import qualified Data.Bytes.Chunks as Chunks
12+
import qualified Data.ByteString.Lazy as LBS
913
import qualified Json as J
1014
import qualified Data.Aeson as Aeson
1115

1216
main :: IO ()
13-
main = defaultMain
14-
[ bgroup "json"
15-
[ bgroup "twitter"
16-
[ bench "100" $ whnf
17-
(\b -> J.decode (Bytes.fromByteArray b))
18-
encodedTwitter100
17+
main = do
18+
valueTwitter100 <- case J.decode (Bytes.fromByteArray encodedTwitter100) of
19+
Left _ -> fail "json-syntax failed to decode twitter-100"
20+
Right v -> pure v
21+
aesonValueTwitter100 <- case Aeson.decodeStrict' byteStringTwitter100 of
22+
Nothing -> fail "aeson failed to decode twitter-100"
23+
Just (v :: Aeson.Value) -> pure v
24+
defaultMain
25+
[ bgroup "json"
26+
[ bgroup "twitter"
27+
[ bgroup "100"
28+
[ bench "decode" $ whnf
29+
(\b -> J.decode (Bytes.fromByteArray b))
30+
encodedTwitter100
31+
, bench "encode" $ whnf
32+
(\v -> Chunks.length (BLDR.run 128 (J.encode v)))
33+
valueTwitter100
34+
]
35+
]
1936
]
20-
]
21-
, bgroup "aeson"
22-
[ bgroup "twitter"
23-
[ bench "100"
24-
(whnf (Aeson.decodeStrict' @Aeson.Value) byteStringTwitter100)
37+
, bgroup "aeson"
38+
[ bgroup "twitter"
39+
[ bgroup "100"
40+
[ bench "decode"
41+
(whnf (Aeson.decodeStrict' @Aeson.Value) byteStringTwitter100)
42+
, bench "encode" $ whnf
43+
(\v -> LBS.length (Aeson.encode v))
44+
aesonValueTwitter100
45+
]
46+
]
2547
]
2648
]
27-
]

json-syntax.cabal

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ library
2929
exposed-modules: Json
3030
build-depends:
3131
, array-builder >=0.1 && <0.2
32-
, array-chunks >=0.1.1 && <0.2
32+
, array-chunks >=0.1.2 && <0.2
3333
, base >=4.12 && <5
3434
, bytebuild >=0.3.4 && <0.4
3535
, byteslice >=0.1.3 && <0.3
@@ -74,6 +74,7 @@ benchmark bench
7474
build-depends:
7575
, aeson
7676
, base
77+
, bytebuild
7778
, byteslice
7879
, bytestring
7980
, gauge

src/Json.hs

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
{-# language NamedFieldPuns #-}
99
{-# language PatternSynonyms #-}
1010
{-# language TypeApplications #-}
11+
{-# language UnboxedSums #-}
1112
{-# language UnboxedTuples #-}
1213

1314
module Json
@@ -20,6 +21,15 @@ module Json
2021
, encode
2122
-- * Infix Synonyms
2223
, pattern (:->)
24+
-- * Construction
25+
, object1
26+
, object2
27+
, object3
28+
, object4
29+
, object5
30+
, object6
31+
, object7
32+
, object8
2333
) where
2434

2535
import Prelude hiding (Bool(True,False))
@@ -35,13 +45,15 @@ import Data.Chunks (Chunks(ChunksNil,ChunksCons))
3545
import Data.Number.Scientific (Scientific)
3646
import Data.Primitive (ByteArray,MutableByteArray)
3747
import Data.Text.Short (ShortText)
38-
import GHC.Exts (Char(C#),Int(I#),gtWord#,ltWord#,word2Int#,chr#)
48+
import GHC.Exts (SmallArray#,Char(C#),Int(I#),gtWord#,ltWord#,word2Int#,chr#)
49+
import GHC.Exts (sizeofSmallArray#,indexSmallArray#)
3950
import GHC.Word (Word8(W8#),Word16(W16#))
4051

4152
import qualified Prelude
4253
import qualified Data.Builder.ST as B
4354
import qualified Data.Bytes.Builder as BLDR
4455
import qualified Data.Bytes.Parser as P
56+
import qualified Data.Chunks as Chunks
4557
import qualified Data.Text.Short.Unsafe as TS
4658
import qualified Data.Number.Scientific as SCI
4759
import qualified Data.Primitive as PM
@@ -139,11 +151,11 @@ encode = \case
139151
String s -> BLDR.shortTextJsonString s
140152
Number n -> SCI.builderUtf8 n
141153
Array ys -> case unconsNonempty ys of
142-
Nothing -> BLDR.ascii2 '[' ']'
143-
Just (x,xs) ->
154+
(# (# #) | #) -> BLDR.ascii2 '[' ']'
155+
(# | (# x, xs #) #) ->
144156
BLDR.ascii '['
145157
<>
146-
encode (PM.indexSmallArray x 0)
158+
encode (case indexSmallArray# x 0# of {(# z #) -> z})
147159
<>
148160
foldrTail
149161
( \v b -> BLDR.ascii ',' <> encode v <> b
@@ -152,13 +164,13 @@ encode = \case
152164
( \v b -> BLDR.ascii ',' <> encode v <> b
153165
) (BLDR.ascii ']') xs
154166
)
155-
x
167+
(PM.SmallArray x)
156168
Object ys -> case unconsNonempty ys of
157-
Nothing -> BLDR.ascii2 '{' '}'
158-
Just (x,xs) ->
169+
(# (# #) | #) -> BLDR.ascii2 '{' '}'
170+
(# | (# x,xs #) #) ->
159171
BLDR.ascii '{'
160172
<>
161-
encodeMember (PM.indexSmallArray x 0)
173+
encodeMember (case indexSmallArray# x 0# of {(# z #) -> z})
162174
<>
163175
foldrTail
164176
( \mbr b -> BLDR.ascii ',' <> encodeMember mbr <> b
@@ -167,7 +179,7 @@ encode = \case
167179
( \mbr b -> BLDR.ascii ',' <> encodeMember mbr <> b
168180
) (BLDR.ascii '}') xs
169181
)
170-
x
182+
(PM.SmallArray x)
171183

172184
encodeMember :: Member -> BLDR.Builder
173185
encodeMember Member{key,value} =
@@ -187,13 +199,13 @@ foldrTail f z !ary = go 1 where
187199
= f x (go (i+1))
188200

189201
-- Get the first non-empty SmallArray from the Chunks.
190-
unconsNonempty :: Chunks a -> Maybe (PM.SmallArray a, Chunks a)
202+
unconsNonempty :: Chunks a -> (# (# #) | (# SmallArray# a, Chunks a #) #)
191203
{-# inline unconsNonempty #-}
192204
unconsNonempty = go where
193-
go ChunksNil = Nothing
194-
go (ChunksCons x xs) = case PM.sizeofSmallArray x of
195-
0 -> go xs
196-
_ -> Just (x,xs)
205+
go ChunksNil = (# (# #) | #)
206+
go (ChunksCons (PM.SmallArray x) xs) = case sizeofSmallArray# x of
207+
0# -> go xs
208+
_ -> (# | (# x, xs #) #)
197209

198210
-- Precondition: skip over all space before calling this.
199211
-- It will not skip leading space for you. It does
@@ -418,3 +430,43 @@ w16ToChar (W16# w) = C# (chr# (word2Int# w))
418430
-- | Infix pattern synonym for 'Member'.
419431
pattern (:->) :: ShortText -> Value -> Member
420432
pattern key :-> value = Member{key,value}
433+
434+
-- | Construct a JSON object with one member.
435+
object1 :: Member -> Value
436+
{-# inline object1 #-}
437+
object1 a = Object (Chunks.singleton a)
438+
439+
-- | Construct a JSON object with two members.
440+
object2 :: Member -> Member -> Value
441+
{-# inline object2 #-}
442+
object2 a b = Object (Chunks.doubleton a b)
443+
444+
-- | Construct a JSON object with three members.
445+
object3 :: Member -> Member -> Member -> Value
446+
{-# inline object3 #-}
447+
object3 a b c = Object (Chunks.tripleton a b c)
448+
449+
-- | Construct a JSON object with four members.
450+
object4 :: Member -> Member -> Member -> Member -> Value
451+
{-# inline object4 #-}
452+
object4 a b c d = Object (Chunks.quadrupleton a b c d)
453+
454+
-- | Construct a JSON object with five members.
455+
object5 :: Member -> Member -> Member -> Member -> Member -> Value
456+
{-# inline object5 #-}
457+
object5 a b c d e = Object (Chunks.quintupleton a b c d e)
458+
459+
-- | Construct a JSON object with six members.
460+
object6 :: Member -> Member -> Member -> Member -> Member -> Member -> Value
461+
{-# inline object6 #-}
462+
object6 a b c d e f = Object (Chunks.sextupleton a b c d e f)
463+
464+
-- | Construct a JSON object with seven members.
465+
object7 :: Member -> Member -> Member -> Member -> Member -> Member -> Member -> Value
466+
{-# inline object7 #-}
467+
object7 a b c d e f g = Object (Chunks.septupleton a b c d e f g)
468+
469+
-- | Construct a JSON object with eight members.
470+
object8 :: Member -> Member -> Member -> Member -> Member -> Member -> Member -> Member -> Value
471+
{-# inline object8 #-}
472+
object8 a b c d e f g h = Object (Chunks.octupleton a b c d e f g h)

0 commit comments

Comments
 (0)