55{-# language DeriveAnyClass #-}
66{-# language LambdaCase #-}
77{-# language MagicHash #-}
8+ {-# language NamedFieldPuns #-}
89{-# language TypeApplications #-}
10+ {-# language UnboxedTuples #-}
911
1012module Json
1113 ( -- * Types
@@ -14,6 +16,7 @@ module Json
1416 , SyntaxException (.. )
1517 -- * Functions
1618 , decode
19+ , encode
1720 ) where
1821
1922import Prelude hiding (Bool (True ,False ))
@@ -25,7 +28,7 @@ import Data.Builder.ST (Builder)
2528import Data.Bytes.Parser (Parser )
2629import Data.Bytes.Types (Bytes (.. ))
2730import Data.Char (ord )
28- import Data.Chunks (Chunks (ChunksNil ))
31+ import Data.Chunks (Chunks (ChunksNil , ChunksCons ))
2932import Data.Number.Scientific (Scientific )
3033import Data.Primitive (ByteArray ,MutableByteArray )
3134import Data.Text.Short (ShortText )
@@ -34,6 +37,7 @@ import GHC.Word (Word8(W8#),Word16(W16#))
3437
3538import qualified Prelude
3639import qualified Data.Builder.ST as B
40+ import qualified Data.Bytes.Builder as BLDR
3741import qualified Data.Bytes.Parser as P
3842import qualified Data.Text.Short.Unsafe as TS
3943import qualified Data.Number.Scientific as SCI
@@ -123,6 +127,70 @@ decode = P.parseBytesEither do
123127 P. endOfInput UnexpectedLeftovers
124128 pure result
125129
130+ encode :: Value -> BLDR. Builder
131+ encode = \ case
132+ True -> BLDR. ascii4 ' t' ' r' ' u' ' e'
133+ False -> BLDR. ascii5 ' f' ' a' ' l' ' s' ' e'
134+ Null -> BLDR. ascii4 ' n' ' u' ' l' ' l'
135+ String s -> BLDR. shortTextJsonString s
136+ Number n -> SCI. builderUtf8 n
137+ Array ys -> case unconsNonempty ys of
138+ Nothing -> BLDR. ascii2 ' [' ' ]'
139+ Just (x,xs) ->
140+ BLDR. ascii ' ['
141+ <>
142+ encode (PM. indexSmallArray x 0 )
143+ <>
144+ foldrTail
145+ ( \ v b -> BLDR. ascii ' ,' <> encode v <> b
146+ )
147+ ( foldr
148+ ( \ v b -> BLDR. ascii ' ,' <> encode v <> b
149+ ) (BLDR. ascii ' ]' ) xs
150+ )
151+ x
152+ Object ys -> case unconsNonempty ys of
153+ Nothing -> BLDR. ascii2 ' {' ' }'
154+ Just (x,xs) ->
155+ BLDR. ascii ' {'
156+ <>
157+ encodeMember (PM. indexSmallArray x 0 )
158+ <>
159+ foldrTail
160+ ( \ mbr b -> BLDR. ascii ' ,' <> encodeMember mbr <> b
161+ )
162+ ( foldr
163+ ( \ mbr b -> BLDR. ascii ' ,' <> encodeMember mbr <> b
164+ ) (BLDR. ascii ' }' ) xs
165+ )
166+ x
167+
168+ encodeMember :: Member -> BLDR. Builder
169+ encodeMember Member {key,value} =
170+ BLDR. shortTextJsonString key
171+ <>
172+ BLDR. ascii ' :'
173+ <>
174+ encode value
175+
176+ foldrTail :: (a -> b -> b ) -> b -> PM. SmallArray a -> b
177+ {-# inline foldrTail #-}
178+ foldrTail f z ! ary = go 1 where
179+ ! sz = PM. sizeofSmallArray ary
180+ go i
181+ | i == sz = z
182+ | (# x # ) <- PM. indexSmallArray## ary i
183+ = f x (go (i+ 1 ))
184+
185+ -- Get the first non-empty SmallArray from the Chunks.
186+ unconsNonempty :: Chunks a -> Maybe (PM. SmallArray a , Chunks a )
187+ {-# inline unconsNonempty #-}
188+ unconsNonempty = go where
189+ go ChunksNil = Nothing
190+ go (ChunksCons x xs) = case PM. sizeofSmallArray x of
191+ 0 -> go xs
192+ _ -> Just (x,xs)
193+
126194-- Precondition: skip over all space before calling this.
127195-- It will not skip leading space for you. It does
128196parser :: Char -> Parser SyntaxException s Value
0 commit comments