on master help sign in
Add Data.Aeson.Encoding.Internal
a523d02Oleg Grenrus
Data/Aeson/Encoding.hs+3−149
@@ -1,165 +1,19 @@
1−{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, DeriveDataTypeable, RankNTypes #-}
2module Data.Aeson.Encoding
1module Data.Aeson.Encoding
3 (
2 (
4 -- * Encoding
3 -- * Encoding
5− Encoding (..) -- TODO: export fromEncoding for now
4+ Encoding
6 , fromEncoding
5 , fromEncoding
7 , unsafeToEncoding
6 , unsafeToEncoding
8− , Series (..) -- TODO: don't export constructor
7+ , Series
9 , pairs
8 , pairs
10 -- * Predicates
9 -- * Predicates
11 , nullEncoding
10 , nullEncoding
12 -- * Encoding constructors
11 -- * Encoding constructors
13 , emptyArray_
12 , emptyArray_
14 , emptyObject_
13 , emptyObject_
15− , wrapArray
16− , wrapObject
17 , text
14 , text
18 , list
15 , list
19 , dict
16 , dict
20− , tuple
21− , (>*<)
22− -- ** chars
23− , comma, colon, openBracket, closeBracket, openCurly, closeCurly
24 ) where
17 ) where
25
18
26−#if !MIN_VERSION_base(4,8,0)
19+import Data.Aeson.Encoding.Internal
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 '}'
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 )
140import Text.Printf ( printf )
140import Text.Printf ( printf )
141import Text.Show ( show )
141import Text.Show ( show )
142import qualified Data.Aeson as A
142import qualified Data.Aeson as A
143−import qualified Data.Aeson.Encoding as E
143+import qualified Data.Aeson.Encoding.Internal as E
144import qualified Data.HashMap.Strict as H ( lookup, toList )
144import qualified Data.HashMap.Strict as H ( lookup, toList )
145import qualified Data.Map as M ( fromList, findWithDefault )
145import qualified Data.Map as M ( fromList, findWithDefault )
146import qualified Data.Text as T ( Text, pack, unpack )
146import qualified Data.Text as T ( Text, pack, unpack )
Data/Aeson/Types/Class.hs+7−15
@@ -55,12 +55,12 @@ module Data.Aeson.Types.Class
55 ) where
55 ) where
56
56
57import Data.Aeson.Types.Internal
57import Data.Aeson.Types.Internal
58+import Data.Aeson.Encoding.Internal (Encoding, Series)
58import Data.Monoid ((<>))
59import Data.Monoid ((<>))
59import Data.Text (Text)
60import Data.Text (Text)
60import GHC.Generics (Generic, Rep, from, to)
61import GHC.Generics (Generic, Rep, from, to)
61−import Data.Aeson.Encoding (Encoding (..), Series (..), emptyArray_, list)
62+import qualified Data.Aeson.Encoding.Internal as E
62−import qualified Data.ByteString.Builder as B
63−import qualified Data.Aeson.Encode.Builder as E
63+import qualified Data.Aeson.Encode.Builder as EB
64import qualified Data.Vector as V
64import qualified Data.Vector as V
65
65
66#if !MIN_VERSION_base(4,8,0)
66#if !MIN_VERSION_base(4,8,0)
@@ -208,7 +208,7 @@ class ToJSON a where
208 -- @
208 -- @
209
209
210 toEncoding :: a -> Encoding
210 toEncoding :: a -> Encoding
211− toEncoding = Encoding . E.encodeToBuilder . toJSON
211+ toEncoding = E.Encoding . EB.encodeToBuilder . toJSON
212 {-# INLINE toEncoding #-}
212 {-# INLINE toEncoding #-}
213
213
214 toJSONList :: [a] -> Value
214 toJSONList :: [a] -> Value
@@ -216,12 +216,7 @@ class ToJSON a where
216 {-# INLINE toJSONList #-}
216 {-# INLINE toJSONList #-}
217
217
218 toEncodingList :: [a] -> Encoding
218 toEncodingList :: [a] -> Encoding
219− toEncodingList [] = emptyArray_
219+ toEncodingList = listEncoding toEncoding
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
225 {-# INLINE toEncodingList #-}
220 {-# INLINE toEncodingList #-}
226
221
227-- | A type that can be converted from JSON, with the possibility of
222-- | A type that can be converted from JSON, with the possibility of
@@ -326,10 +321,7 @@ class KeyValue kv where
326 infixr 8 .=
321 infixr 8 .=
327
322
328instance KeyValue Series where
323instance KeyValue Series where
329− name .= value = Value . Encoding $
324+ name .= value = E.Value $ E.text name <> E.colon <> toEncoding value
330− E.text name <> B.char7 ':' <> builder value
331− where
332− builder = fromEncoding . toEncoding
333 {-# INLINE (.=) #-}
325 {-# INLINE (.=) #-}
334
326
335instance KeyValue Pair where
327instance KeyValue Pair where
@@ -536,7 +528,7 @@ toEncoding2 = liftToEncoding2 toEncoding toEncodingList toEncoding toEncodingLis
536-------------------------------------------------------------------------------
528-------------------------------------------------------------------------------
537
529
538listEncoding :: (a -> Encoding) -> [a] -> Encoding
530listEncoding :: (a -> Encoding) -> [a] -> Encoding
539−listEncoding = list
531+listEncoding = E.list
540
532
541listValue :: (a -> Value) -> [a] -> Value
533listValue :: (a -> Value) -> [a] -> Value
542listValue f = Array . V.fromList . map f
534listValue f = Array . V.fromList . map f
Data/Aeson/Types/Generic.hs+2−2
@@ -24,8 +24,8 @@ module Data.Aeson.Types.Generic ( ) where
24import Control.Applicative ((<|>))
24import Control.Applicative ((<|>))
25import Control.Monad ((<=<))
25import Control.Monad ((<=<))
26import Control.Monad.ST (ST)
26import Control.Monad.ST (ST)
27−import Data.Aeson.Encoding (Encoding, (>*<))
27+import Data.Aeson.Encoding.Internal (Encoding, (>*<))
28−import qualified Data.Aeson.Encoding as E
28+import qualified Data.Aeson.Encoding.Internal as E
29import Data.Aeson.Types.Instances
29import Data.Aeson.Types.Instances
30import Data.Aeson.Types.Internal
30import Data.Aeson.Types.Internal
31import Data.Bits (unsafeShiftR)
31import Data.Bits (unsafeShiftR)
Data/Aeson/Types/Instances.hs+1−1
@@ -87,7 +87,7 @@ module Data.Aeson.Types.Instances
87
87
88import Data.Aeson.Types.Instances.Tuple ()
88import Data.Aeson.Types.Instances.Tuple ()
89
89
90−import Data.Aeson.Encoding (Encoding (..), emptyArray_, dict, tuple, (>*<))
90+import Data.Aeson.Encoding.Internal (Encoding (..), emptyArray_, dict, tuple, (>*<))
91
91
92import Control.Applicative (Const(..))
92import Control.Applicative (Const(..))
93import Data.Aeson.Encode.Functions (builder)
93import Data.Aeson.Encode.Functions (builder)
Data/Aeson/Types/Instances/Tuple.hs+1−2
@@ -12,10 +12,9 @@
12-- Tuple instances
12-- Tuple instances
13module Data.Aeson.Types.Instances.Tuple () where
13module Data.Aeson.Types.Instances.Tuple () where
14
14
15−import Data.Aeson.Encoding (tuple, (>*<))
15+import Data.Aeson.Encoding.Internal (tuple, (>*<))
16import Data.Aeson.Types.Class
16import Data.Aeson.Types.Class
17import Data.Aeson.Types.Internal
17import Data.Aeson.Types.Internal
18−import Data.Monoid ((<>))
19
18
20import qualified Data.Vector as V
19import qualified Data.Vector as V
21import qualified Data.Vector.Mutable as VM (unsafeNew, unsafeWrite)
20import qualified Data.Vector.Mutable as VM (unsafeNew, unsafeWrite)
aeson.cabal+1
@@ -71,6 +71,7 @@ library
71 Data.Aeson
71 Data.Aeson
72 Data.Aeson.Encode
72 Data.Aeson.Encode
73 Data.Aeson.Encoding
73 Data.Aeson.Encoding
74+ Data.Aeson.Encoding.Internal
74 Data.Aeson.Internal
75 Data.Aeson.Internal
75 Data.Aeson.Internal.Time
76 Data.Aeson.Internal.Time
76 Data.Aeson.Parser
77 Data.Aeson.Parser
benchmarks/aeson-benchmarks.cabal+1
@@ -16,6 +16,7 @@ library
16 exposed-modules:
16 exposed-modules:
17 Data.Aeson
17 Data.Aeson
18 Data.Aeson.Encoding
18 Data.Aeson.Encoding
19+ Data.Aeson.Encoding.Internal
19 Data.Aeson.Encode
20 Data.Aeson.Encode
20 Data.Aeson.Encode.Builder
21 Data.Aeson.Encode.Builder
21 Data.Aeson.Encode.Functions
22 Data.Aeson.Encode.Functions