on master help sign in
Add Data.Aeson.Encoding.Internal
commita523d026b2dfbf5b265a49f4f4df3ec38d25ede3 authorOleg Grenrus <oleg.grenrus@iki.fi> dateMon 6 Jun 2016, 10:00 (10 years ago) parent5a0faf7
9 files changed+183−170
Data/Aeson/Encoding.hs+3−149 Data/Aeson/Encoding/Internal.hs+166 Data/Aeson/TH.hs+1−1 Data/Aeson/Types/Class.hs+7−15 Data/Aeson/Types/Generic.hs+2−2 Data/Aeson/Types/Instances.hs+1−1 Data/Aeson/Types/Instances/Tuple.hs+1−2 aeson.cabal+1 benchmarks/aeson-benchmarks.cabal+1
Data/Aeson/Encoding.hs+3−149
@@ -1,165 +1,19 @@
1−{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, DeriveDataTypeable, RankNTypes #-}
21module Data.Aeson.Encoding
32 (
43 -- * Encoding
5− Encoding (..) -- TODO: export fromEncoding for now
4+ Encoding
65 , fromEncoding
76 , unsafeToEncoding
8− , Series (..) -- TODO: don't export constructor
7+ , Series
98 , pairs
109 -- * Predicates
1110 , nullEncoding
1211 -- * Encoding constructors
1312 , emptyArray_
1413 , emptyObject_
15− , wrapArray
16− , wrapObject
1714 , text
1815 , list
1916 , dict
20− , tuple
21− , (>*<)
22− -- ** chars
23− , comma, colon, openBracket, closeBracket, openCurly, closeCurly
2417 ) where
2518
26−#if !MIN_VERSION_base(4,8,0)
27−import Data.Monoid (Monoid(..))
28−#endif
29−
30−import Data.Text (Text)
31−import Data.ByteString.Builder (Builder, char7, toLazyByteString)
32−import Data.Semigroup (Semigroup((<>)))
33−import Data.ByteString.Builder.Prim (primBounded)
34−import Data.Typeable (Typeable)
35−
36−import qualified Data.ByteString.Lazy as BSL
37−import qualified Data.Aeson.Encode.Builder as B
38−
39−-- | An encoding of a JSON value.
40−newtype Encoding = Encoding {
41− fromEncoding :: Builder
42− -- ^ Acquire the underlying bytestring builder.
43− } deriving (Semigroup,Monoid,Typeable)
44−
45−-- | Make Encoding from Builder.
46−--
47−-- Use with care! You have to make sure that the passed Builder
48−-- is a valid JSON Encoding!
49−unsafeToEncoding :: Builder -> Encoding
50−unsafeToEncoding = Encoding
51−
52−-------------------------------------------------------------------------------
53−-- Encoding instances
54−-------------------------------------------------------------------------------
55−
56−instance Show Encoding where
57− show (Encoding e) = show (toLazyByteString e)
58−
59−instance Eq Encoding where
60− Encoding a == Encoding b = toLazyByteString a == toLazyByteString b
61−
62−instance Ord Encoding where
63− compare (Encoding a) (Encoding b) =
64− compare (toLazyByteString a) (toLazyByteString b)
65−
66−-- | A series of values that, when encoded, should be separated by
67−-- commas. Since 0.11.0.0, the '.=' operator is overloaded to create
68−-- either @(Text, Value)@ or 'Series'. You can use Series when
69−-- encoding directly to a bytestring builder as in the following
70−-- example:
71−--
72−-- > toEncoding (Person name age) = pairs ("name" .= name <> "age" .= age)
73−data Series = Empty
74− | Value Encoding
75− deriving (Typeable)
76−
77−instance Semigroup Series where
78− Empty <> a = a
79− Value a <> b =
80− Value $
81− a <> case b of
82− Empty -> mempty
83− Value c -> Encoding (char7 ',') <> c
84−
85−instance Monoid Series where
86− mempty = Empty
87− mappend = (<>)
88−
89−nullEncoding :: Encoding -> Bool
90−nullEncoding = BSL.null . toLazyByteString . fromEncoding
91−
92−emptyArray_ :: Encoding
93−emptyArray_ = Encoding B.emptyArray_
94−
95−emptyObject_ :: Encoding
96−emptyObject_ = Encoding B.emptyObject_
97−
98−wrapArray :: Encoding -> Encoding
99−wrapArray e = openBracket <> e <> closeBracket
100−
101−wrapObject :: Encoding -> Encoding
102−wrapObject e = openCurly <> e <> closeCurly
103−
104−-- | Encode a series of key/value pairs, separated by commas.
105−pairs :: Series -> Encoding
106−pairs = brackets '{' '}'
107−{-# INLINE pairs #-}
108−
109−brackets :: Char -> Char -> Series -> Encoding
110−brackets begin end (Value v) = Encoding $
111− char7 begin <> fromEncoding v <> char7 end
112−brackets begin end Empty = Encoding (primBounded (B.ascii2 (begin,end)) ())
113−
114−list :: (a -> Encoding) -> [a] -> Encoding
115−list _ [] = emptyArray_
116−list to' (x:xs) = Encoding $
117− char7 '[' <> fromEncoding (to' x) <> commas xs <> char7 ']'
118− where
119− commas = foldr (\v vs -> char7 ',' <> fromEncoding (to' v) <> vs) mempty
120−{-# INLINE list #-}
121−
122−-- | Encode as JSON object
123−dict
124− :: (k -> Encoding) -- ^ key encoding
125− -> (v -> Encoding) -- ^ value encoding
126− -> (forall a. (k -> v -> a -> a) -> a -> m -> a) -- ^ @foldrWithKey@ - indexed fold
127− -> m -- ^ container
128− -> Encoding
129−dict encodeKey encodeVal foldrWithKey = brackets '{' '}' . foldrWithKey go mempty
130− where
131− go k v c = Value (encodeKV k v) <> c
132− encodeKV k v = encodeKey k <> Encoding (char7 ':') <> encodeVal v
133−{-# INLINE dict #-}
134−
135−infixr 6 >*<
136−-- | See 'tuple'.
137−(>*<) :: Encoding -> Encoding -> Encoding
138−Encoding a >*< Encoding b = Encoding $ a <> char7 ',' <> b
139−{-# INLINE (>*<) #-}
140−
141−-- | Encode as a tuple.
142−--
143−-- @
144−-- toEncoding (X a b c) = tuple $
145−-- toEncoding a >*<
146−-- toEncoding b >*<
147−-- toEncoding c
148−tuple :: Encoding -> Encoding
149−tuple b = Encoding (char7 '[' <> fromEncoding b <> char7 ']')
150−{-# INLINE tuple #-}
151−
152−text :: Text -> Encoding
153−text = Encoding . B.text
154−
155−-------------------------------------------------------------------------------
156−-- chars
157−-------------------------------------------------------------------------------
158−
159−comma, colon, openBracket, closeBracket, openCurly, closeCurly :: Encoding
160−comma = Encoding $ char7 ','
161−colon = Encoding $ char7 ':'
162−openBracket = Encoding $ char7 '['
163−closeBracket = Encoding $ char7 ']'
164−openCurly = Encoding $ char7 '{'
165−closeCurly = Encoding $ char7 '}'
19+import Data.Aeson.Encoding.Internal
Data/Aeson/Encoding/Internal.hsnew file+166
@@ -0,0 +1,166 @@
1+{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, DeriveDataTypeable, RankNTypes #-}
2+module Data.Aeson.Encoding.Internal
3+ (
4+ -- * Encoding
5+ Encoding (..) -- TODO: export fromEncoding for now
6+ , fromEncoding
7+ , unsafeToEncoding
8+ , Series (..) -- TODO: don't export constructor
9+ , pairs
10+ -- * Predicates
11+ , nullEncoding
12+ -- * Encoding constructors
13+ , emptyArray_
14+ , emptyObject_
15+ , wrapArray
16+ , wrapObject
17+ , text
18+ , list
19+ , dict
20+ , tuple
21+ , (>*<)
22+ -- ** chars
23+ , comma, colon, openBracket, closeBracket, openCurly, closeCurly
24+ ) where
25+
26+#if !MIN_VERSION_base(4,8,0)
27+import Data.Monoid (Monoid(..))
28+#endif
29+
30+import Data.Text (Text)
31+import Data.ByteString.Builder (Builder, char7, toLazyByteString)
32+import Data.Semigroup (Semigroup((<>)))
33+import Data.ByteString.Builder.Prim (primBounded)
34+import Data.Typeable (Typeable)
35+
36+import qualified Data.ByteString.Lazy as BSL
37+import qualified Data.Aeson.Encode.Builder as B
38+
39+-- | An encoding of a JSON value.
40+newtype Encoding = Encoding {
41+ fromEncoding :: Builder
42+ -- ^ Acquire the underlying bytestring builder.
43+ } deriving (Semigroup,Monoid,Typeable)
44+
45+-- | Make Encoding from Builder.
46+--
47+-- Use with care! You have to make sure that the passed Builder
48+-- is a valid JSON Encoding!
49+unsafeToEncoding :: Builder -> Encoding
50+unsafeToEncoding = Encoding
51+
52+-------------------------------------------------------------------------------
53+-- Encoding instances
54+-------------------------------------------------------------------------------
55+
56+instance Show Encoding where
57+ show (Encoding e) = show (toLazyByteString e)
58+
59+instance Eq Encoding where
60+ Encoding a == Encoding b = toLazyByteString a == toLazyByteString b
61+
62+instance Ord Encoding where
63+ compare (Encoding a) (Encoding b) =
64+ compare (toLazyByteString a) (toLazyByteString b)
65+
66+-- | A series of values that, when encoded, should be separated by
67+-- commas. Since 0.11.0.0, the '.=' operator is overloaded to create
68+-- either @(Text, Value)@ or 'Series'. You can use Series when
69+-- encoding directly to a bytestring builder as in the following
70+-- example:
71+--
72+-- > toEncoding (Person name age) = pairs ("name" .= name <> "age" .= age)
73+data Series = Empty
74+ | Value Encoding
75+ deriving (Typeable)
76+
77+instance Semigroup Series where
78+ Empty <> a = a
79+ Value a <> b =
80+ Value $
81+ a <> case b of
82+ Empty -> mempty
83+ Value c -> Encoding (char7 ',') <> c
84+
85+instance Monoid Series where
86+ mempty = Empty
87+ mappend = (<>)
88+
89+nullEncoding :: Encoding -> Bool
90+nullEncoding = BSL.null . toLazyByteString . fromEncoding
91+
92+emptyArray_ :: Encoding
93+emptyArray_ = Encoding B.emptyArray_
94+
95+emptyObject_ :: Encoding
96+emptyObject_ = Encoding B.emptyObject_
97+
98+wrapArray :: Encoding -> Encoding
99+wrapArray e = openBracket <> e <> closeBracket
100+
101+wrapObject :: Encoding -> Encoding
102+wrapObject e = openCurly <> e <> closeCurly
103+
104+-- | Encode a series of key/value pairs, separated by commas.
105+pairs :: Series -> Encoding
106+pairs = brackets '{' '}'
107+{-# INLINE pairs #-}
108+
109+brackets :: Char -> Char -> Series -> Encoding
110+brackets begin end (Value v) = Encoding $
111+ char7 begin <> fromEncoding v <> char7 end
112+brackets begin end Empty = Encoding (primBounded (B.ascii2 (begin,end)) ())
113+
114+list :: (a -> Encoding) -> [a] -> Encoding
115+list _ [] = emptyArray_
116+list to' (x:xs) = Encoding $
117+ char7 '[' <> fromEncoding (to' x) <> commas xs <> char7 ']'
118+ where
119+ commas = foldr (\v vs -> char7 ',' <> fromEncoding (to' v) <> vs) mempty
120+{-# INLINE list #-}
121+
122+-- | Encode as JSON object
123+dict
124+ :: (k -> Encoding) -- ^ key encoding
125+ -> (v -> Encoding) -- ^ value encoding
126+ -> (forall a. (k -> v -> a -> a) -> a -> m -> a) -- ^ @foldrWithKey@ - indexed fold
127+ -> m -- ^ container
128+ -> Encoding
129+dict encodeKey encodeVal foldrWithKey = brackets '{' '}' . foldrWithKey go mempty
130+ where
131+ go k v c = Value (encodeKV k v) <> c
132+ encodeKV k v = encodeKey k <> Encoding (char7 ':') <> encodeVal v
133+{-# INLINE dict #-}
134+
135+infixr 6 >*<
136+-- | See 'tuple'.
137+(>*<) :: Encoding -> Encoding -> Encoding
138+Encoding a >*< Encoding b = Encoding $ a <> char7 ',' <> b
139+{-# INLINE (>*<) #-}
140+
141+-- | Encode as a tuple.
142+--
143+-- @
144+-- toEncoding (X a b c) = tuple $
145+-- toEncoding a >*<
146+-- toEncoding b >*<
147+-- toEncoding c
148+tuple :: Encoding -> Encoding
149+tuple b = Encoding (char7 '[' <> fromEncoding b <> char7 ']')
150+{-# INLINE tuple #-}
151+
152+text :: Text -> Encoding
153+text = Encoding . B.text
154+
155+-------------------------------------------------------------------------------
156+-- chars
157+-------------------------------------------------------------------------------
158+
159+comma, colon, openBracket, closeBracket, openCurly, closeCurly :: Encoding
160+comma = Encoding $ char7 ','
161+colon = Encoding $ char7 ':'
162+openBracket = Encoding $ char7 '['
163+closeBracket = Encoding $ char7 ']'
164+openCurly = Encoding $ char7 '{'
165+closeCurly = Encoding $ char7 '}'
166+
Data/Aeson/TH.hs+1−1
@@ -140,7 +140,7 @@ import Prelude ( head )
140140import Text.Printf ( printf )
141141import Text.Show ( show )
142142import qualified Data.Aeson as A
143−import qualified Data.Aeson.Encoding as E
143+import qualified Data.Aeson.Encoding.Internal as E
144144import qualified Data.HashMap.Strict as H ( lookup, toList )
145145import qualified Data.Map as M ( fromList, findWithDefault )
146146import qualified Data.Text as T ( Text, pack, unpack )
Data/Aeson/Types/Class.hs+7−15
@@ -55,12 +55,12 @@ module Data.Aeson.Types.Class
5555 ) where
5656
5757import Data.Aeson.Types.Internal
58+import Data.Aeson.Encoding.Internal (Encoding, Series)
5859import Data.Monoid ((<>))
5960import Data.Text (Text)
6061import GHC.Generics (Generic, Rep, from, to)
61−import Data.Aeson.Encoding (Encoding (..), Series (..), emptyArray_, list)
62−import qualified Data.ByteString.Builder as B
63−import qualified Data.Aeson.Encode.Builder as E
62+import qualified Data.Aeson.Encoding.Internal as E
63+import qualified Data.Aeson.Encode.Builder as EB
6464import qualified Data.Vector as V
6565
6666#if !MIN_VERSION_base(4,8,0)
@@ -208,7 +208,7 @@ class ToJSON a where
208208 -- @
209209
210210 toEncoding :: a -> Encoding
211− toEncoding = Encoding . E.encodeToBuilder . toJSON
211+ toEncoding = E.Encoding . EB.encodeToBuilder . toJSON
212212 {-# INLINE toEncoding #-}
213213
214214 toJSONList :: [a] -> Value
@@ -216,12 +216,7 @@ class ToJSON a where
216216 {-# INLINE toJSONList #-}
217217
218218 toEncodingList :: [a] -> Encoding
219− toEncodingList [] = emptyArray_
220− toEncodingList (x:xs) = Encoding $
221− B.char7 '[' <> builder x <> commas xs <> B.char7 ']'
222− where
223− commas = foldr (\v vs -> B.char7 ',' <> builder v <> vs) mempty
224− builder = fromEncoding . toEncoding
219+ toEncodingList = listEncoding toEncoding
225220 {-# INLINE toEncodingList #-}
226221
227222-- | A type that can be converted from JSON, with the possibility of
@@ -326,10 +321,7 @@ class KeyValue kv where
326321 infixr 8 .=
327322
328323instance KeyValue Series where
329− name .= value = Value . Encoding $
330− E.text name <> B.char7 ':' <> builder value
331− where
332− builder = fromEncoding . toEncoding
324+ name .= value = E.Value $ E.text name <> E.colon <> toEncoding value
333325 {-# INLINE (.=) #-}
334326
335327instance KeyValue Pair where
@@ -536,7 +528,7 @@ toEncoding2 = liftToEncoding2 toEncoding toEncodingList toEncoding toEncodingLis
536528-------------------------------------------------------------------------------
537529
538530listEncoding :: (a -> Encoding) -> [a] -> Encoding
539−listEncoding = list
531+listEncoding = E.list
540532
541533listValue :: (a -> Value) -> [a] -> Value
542534listValue f = Array . V.fromList . map f
Data/Aeson/Types/Generic.hs+2−2
@@ -24,8 +24,8 @@ module Data.Aeson.Types.Generic ( ) where
2424import Control.Applicative ((<|>))
2525import Control.Monad ((<=<))
2626import Control.Monad.ST (ST)
27−import Data.Aeson.Encoding (Encoding, (>*<))
28−import qualified Data.Aeson.Encoding as E
27+import Data.Aeson.Encoding.Internal (Encoding, (>*<))
28+import qualified Data.Aeson.Encoding.Internal as E
2929import Data.Aeson.Types.Instances
3030import Data.Aeson.Types.Internal
3131import Data.Bits (unsafeShiftR)
Data/Aeson/Types/Instances.hs+1−1
@@ -87,7 +87,7 @@ module Data.Aeson.Types.Instances
8787
8888import Data.Aeson.Types.Instances.Tuple ()
8989
90−import Data.Aeson.Encoding (Encoding (..), emptyArray_, dict, tuple, (>*<))
90+import Data.Aeson.Encoding.Internal (Encoding (..), emptyArray_, dict, tuple, (>*<))
9191
9292import Control.Applicative (Const(..))
9393import Data.Aeson.Encode.Functions (builder)
Data/Aeson/Types/Instances/Tuple.hs+1−2
@@ -12,10 +12,9 @@
1212-- Tuple instances
1313module Data.Aeson.Types.Instances.Tuple () where
1414
15−import Data.Aeson.Encoding (tuple, (>*<))
15+import Data.Aeson.Encoding.Internal (tuple, (>*<))
1616import Data.Aeson.Types.Class
1717import Data.Aeson.Types.Internal
18−import Data.Monoid ((<>))
1918
2019import qualified Data.Vector as V
2120import qualified Data.Vector.Mutable as VM (unsafeNew, unsafeWrite)
aeson.cabal+1
@@ -71,6 +71,7 @@ library
7171 Data.Aeson
7272 Data.Aeson.Encode
7373 Data.Aeson.Encoding
74+ Data.Aeson.Encoding.Internal
7475 Data.Aeson.Internal
7576 Data.Aeson.Internal.Time
7677 Data.Aeson.Parser
benchmarks/aeson-benchmarks.cabal+1
@@ -16,6 +16,7 @@ library
1616 exposed-modules:
1717 Data.Aeson
1818 Data.Aeson.Encoding
19+ Data.Aeson.Encoding.Internal
1920 Data.Aeson.Encode
2021 Data.Aeson.Encode.Builder
2122 Data.Aeson.Encode.Functions