on master help sign in
Merge pull request #436 from phadej/ryan-master

GHC 7.4/7.6 bundled-bytestring

commit334e5a274b6dd23b1a543ef23c2b4895f8e06eed authorAdam Bergmark <adam@bergmark.nl> dateTue 28 Jun 2016, 22:48 (10 years ago) parentd1b468c20f4bf0
7 files changed+168−23
.travis.yml+4−4 Data/Aeson/Encoding/Builder.hs+75−3 Data/Aeson/Parser/Internal.hs+3−2 Data/Aeson/Types/ToJSON.hs+42−2 aeson.cabal+20−5 benchmarks/aeson-benchmarks.cabal+19−4 stack.yaml+5−3
.travis.yml+4−4
@@ -19,12 +19,12 @@ before_cache:
1919
2020matrix:
2121 include:
22− - env: BUILD=cabal CABALVER=1.16 GHCVER=7.4.2
22+ - env: BUILD=cabal CABALVER=1.18 GHCVER=7.4.2
2323 compiler: ": #GHC 7.4.2"
24− addons: {apt: {packages: [cabal-install-1.16,ghc-7.4.2], sources: [hvr-ghc]}}
25− - env: BUILD=cabal CABALVER=1.16 GHCVER=7.6.3
24+ addons: {apt: {packages: [cabal-install-1.18,ghc-7.4.2], sources: [hvr-ghc]}}
25+ - env: BUILD=cabal CABALVER=1.18 GHCVER=7.6.3
2626 compiler: ": #GHC 7.6.3"
27− addons: {apt: {packages: [cabal-install-1.16,ghc-7.6.3], sources: [hvr-ghc]}}
27+ addons: {apt: {packages: [cabal-install-1.18,ghc-7.6.3], sources: [hvr-ghc]}}
2828 - env: BUILD=cabal CABALVER=1.18 GHCVER=7.8.4
2929 compiler: ": #GHC 7.8.4"
3030 addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
Data/Aeson/Encoding/Builder.hs+75−3
@@ -1,4 +1,4 @@
1−{-# LANGUAGE BangPatterns, OverloadedStrings #-}
1+{-# LANGUAGE BangPatterns, CPP, OverloadedStrings #-}
22
33-- |
44-- Module: Data.Aeson.Encoding.Builder
@@ -52,9 +52,22 @@ import Data.Time.LocalTime
5252import Data.Word (Word8)
5353import qualified Data.HashMap.Strict as HMS
5454import qualified Data.Text as T
55−import qualified Data.Text.Encoding as TE
5655import qualified Data.Vector as V
5756
57+#if MIN_VERSION_bytestring(0,10,4)
58+import Data.Text.Encoding (encodeUtf8BuilderEscaped)
59+#else
60+import Data.Bits ((.&.))
61+import Data.Text.Internal (Text(..))
62+import Data.Text.Internal.Unsafe.Shift (shiftR)
63+import Foreign.Ptr (minusPtr, plusPtr)
64+import Foreign.Storable (poke)
65+import qualified Data.ByteString.Builder.Internal as B
66+import qualified Data.ByteString.Builder.Prim.Internal as BP
67+import qualified Data.Text.Array as A
68+import qualified Data.Text.Internal.Encoding.Utf16 as U16
69+#endif
70+
5871-- | Encode a JSON value to a "Data.ByteString" 'B.Builder'.
5972--
6073-- Use this function if you are encoding over the wire, or need to
@@ -101,7 +114,7 @@ text t = B.char8 '"' <> unquoted t <> B.char8 '"'
101114
102115-- | Encode a JSON string, without enclosing quotes.
103116unquoted :: T.Text -> Builder
104−unquoted = TE.encodeUtf8BuilderEscaped escapeAscii
117+unquoted = encodeUtf8BuilderEscaped escapeAscii
105118
106119-- | Add quotes surrounding a builder
107120quote :: Builder -> Builder
@@ -254,3 +267,62 @@ twoDigits a = T (digit hi) (digit lo)
254267
255268digit :: Int -> Char
256269digit x = chr (x + 48)
270+
271+#if !(MIN_VERSION_bytestring(0,10,4))
272+-- | Encode text using UTF-8 encoding and escape the ASCII characters using
273+-- a 'BP.BoundedPrim'.
274+--
275+-- Use this function is to implement efficient encoders for text-based formats
276+-- like JSON or HTML.
277+{-# INLINE encodeUtf8BuilderEscaped #-}
278+-- TODO: Extend documentation with references to source code in @blaze-html@
279+-- or @aeson@ that uses this function.
280+encodeUtf8BuilderEscaped :: BP.BoundedPrim Word8 -> Text -> B.Builder
281+encodeUtf8BuilderEscaped be =
282+ -- manual eta-expansion to ensure inlining works as expected
283+ \txt -> B.builder (mkBuildstep txt)
284+ where
285+ bound = max 4 $ BP.sizeBound be
286+
287+ mkBuildstep (Text arr off len) !k =
288+ outerLoop off
289+ where
290+ iend = off + len
291+
292+ outerLoop !i0 !br@(B.BufferRange op0 ope)
293+ | i0 >= iend = k br
294+ | outRemaining > 0 = goPartial (i0 + min outRemaining inpRemaining)
295+ -- TODO: Use a loop with an integrated bound's check if outRemaining
296+ -- is smaller than 8, as this will save on divisions.
297+ | otherwise = return $ B.bufferFull bound op0 (outerLoop i0)
298+ where
299+ outRemaining = (ope `minusPtr` op0) `div` bound
300+ inpRemaining = iend - i0
301+
302+ goPartial !iendTmp = go i0 op0
303+ where
304+ go !i !op
305+ | i < iendTmp = case A.unsafeIndex arr i of
306+ w | w <= 0x7F -> do
307+ BP.runB be (fromIntegral w) op >>= go (i + 1)
308+ | w <= 0x7FF -> do
309+ poke8 0 $ (w `shiftR` 6) + 0xC0
310+ poke8 1 $ (w .&. 0x3f) + 0x80
311+ go (i + 1) (op `plusPtr` 2)
312+ | 0xD800 <= w && w <= 0xDBFF -> do
313+ let c = ord $ U16.chr2 w (A.unsafeIndex arr (i+1))
314+ poke8 0 $ (c `shiftR` 18) + 0xF0
315+ poke8 1 $ ((c `shiftR` 12) .&. 0x3F) + 0x80
316+ poke8 2 $ ((c `shiftR` 6) .&. 0x3F) + 0x80
317+ poke8 3 $ (c .&. 0x3F) + 0x80
318+ go (i + 2) (op `plusPtr` 4)
319+ | otherwise -> do
320+ poke8 0 $ (w `shiftR` 12) + 0xE0
321+ poke8 1 $ ((w `shiftR` 6) .&. 0x3F) + 0x80
322+ poke8 2 $ (w .&. 0x3F) + 0x80
323+ go (i + 1) (op `plusPtr` 3)
324+ | otherwise =
325+ outerLoop i (B.BufferRange op ope)
326+ where
327+ poke8 j v = poke (op `plusPtr` j) (fromIntegral v :: Word8)
328+#endif
Data/Aeson/Parser/Internal.hs+3−2
@@ -48,6 +48,7 @@ import Data.Text.Internal.Unsafe.Char (ord)
4848import Data.Vector as Vector (Vector, empty, fromList, reverse)
4949import Data.Word (Word8)
5050import Foreign.ForeignPtr (withForeignPtr)
51+import Foreign.Marshal.Utils (copyBytes)
5152import Foreign.Ptr (Ptr, minusPtr, plusPtr)
5253import Foreign.Storable (poke)
5354import System.IO.Unsafe (unsafePerformIO)
@@ -261,7 +262,7 @@ unescape s = unsafePerformIO $ do
261262 let newlen = p `minusPtr` ptr
262263 slop = len - newlen
263264 Right <$> if slop >= 128 && slop >= len `quot` 4
264− then B.create newlen $ \np -> B.memcpy np ptr newlen
265+ then B.create newlen $ \np -> copyBytes np ptr newlen
265266 else return (PS fp 0 newlen)
266267 where
267268 go ptr = do
@@ -380,7 +381,7 @@ word8 w ptr = do
380381copy :: ByteString -> Ptr Word8 -> Z.ZeptoT IO (Ptr Word8)
381382copy (PS fp off len) ptr =
382383 liftIO . withForeignPtr fp $ \src -> do
383− B.memcpy ptr (src `plusPtr` off) len
384+ copyBytes ptr (src `plusPtr` off) len
384385 return $! ptr `plusPtr` len
385386
386387charUtf8 :: Char -> Ptr Word8 -> Z.ZeptoT IO (Ptr Word8)
Data/Aeson/Types/ToJSON.hs+42−2
@@ -1,3 +1,4 @@
1+{-# LANGUAGE BangPatterns #-}
12{-# LANGUAGE CPP #-}
23{-# LANGUAGE DataKinds #-}
34{-# LANGUAGE DefaultSignatures #-}
@@ -97,6 +98,7 @@ import Foreign.Storable (Storable)
9798import GHC.Generics
9899import Numeric.Natural (Natural)
99100
101+import qualified Data.ByteString as S
100102import qualified Data.ByteString.Lazy as L
101103import qualified Data.DList as DList
102104import qualified Data.HashMap.Strict as H
@@ -119,6 +121,15 @@ import qualified Data.Vector.Primitive as VP
119121import qualified Data.Vector.Storable as VS
120122import qualified Data.Vector.Unboxed as VU
121123
124+#if !(MIN_VERSION_bytestring(0,10,0))
125+import Foreign.ForeignPtr (withForeignPtr)
126+import Foreign.Marshal.Utils (copyBytes)
127+import Foreign.Ptr (plusPtr)
128+
129+import qualified Data.ByteString.Internal as S
130+import qualified Data.ByteString.Lazy.Internal as L
131+#endif
132+
122133toJSONPair :: (a -> Value) -> (b -> Value) -> (a, b) -> Value
123134toJSONPair a b = liftToJSON2 a (listValue a) b (listValue b)
124135{-# INLINE toJSONPair #-}
@@ -454,7 +465,11 @@ toJSONKeyTextEnc :: (a -> Encoding' Text) -> ToJSONKeyFunction a
454465toJSONKeyTextEnc e = ToJSONKeyText tot e
455466 where
456467 -- TODO: dropAround is also used in stringEncoding, which is unfortunate atm
457− tot = T.dropAround (== '"') . T.decodeLatin1 . L.toStrict . E.encodingToLazyByteString . e
468+ tot = T.dropAround (== '"')
469+ . T.decodeLatin1
470+ . lazyToStrictByteString
471+ . E.encodingToLazyByteString
472+ . e
458473
459474-- | Contravariant map, as 'ToJSONKeyFunction' is a contravariant functor.
460475contramapToJSONKeyFunction :: (b -> a) -> ToJSONKeyFunction a -> ToJSONKeyFunction b
@@ -1926,7 +1941,7 @@ stringEncoding :: Encoding' Text -> Value
19261941stringEncoding = String
19271942 . T.dropAround (== '"')
19281943 . T.decodeLatin1
1929− . L.toStrict
1944+ . lazyToStrictByteString
19301945 . E.encodingToLazyByteString
19311946{-# INLINE stringEncoding #-}
19321947
@@ -2566,3 +2581,28 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
25662581 {-# INLINE toJSON #-}
25672582 toEncoding = toEncoding2
25682583 {-# INLINE toEncoding #-}
2584+
2585+-------------------------------------------------------------------------------
2586+-- pre-bytestring-0.10 compatibility
2587+-------------------------------------------------------------------------------
2588+
2589+{-# INLINE lazyToStrictByteString #-}
2590+lazyToStrictByteString :: L.ByteString -> S.ByteString
2591+#if MIN_VERSION_bytestring(0,10,0)
2592+lazyToStrictByteString = L.toStrict
2593+#else
2594+lazyToStrictByteString = packChunks
2595+
2596+-- packChunks is taken from the blaze-builder package.
2597+
2598+-- | Pack the chunks of a lazy bytestring into a single strict bytestring.
2599+packChunks :: L.ByteString -> S.ByteString
2600+packChunks lbs = do
2601+ S.unsafeCreate (fromIntegral $ L.length lbs) (copyChunks lbs)
2602+ where
2603+ copyChunks !L.Empty !_pf = return ()
2604+ copyChunks !(L.Chunk (S.PS fpbuf o l) lbs') !pf = do
2605+ withForeignPtr fpbuf $ \pbuf ->
2606+ copyBytes pf (pbuf `plusPtr` o) l
2607+ copyChunks lbs' (pf `plusPtr` l)
2608+#endif
aeson.cabal+20−5
@@ -58,6 +58,11 @@ flag developer
5858 default: False
5959 manual: True
6060
61+flag bytestring-builder
62+ description: Depend on the bytestring-builder package for backwards compatibility.
63+ default: False
64+ manual: False
65+
6166library
6267 default-language: Haskell2010
6368
@@ -92,13 +97,12 @@ library
9297 attoparsec >= 0.13.0.1,
9398 base >= 4.5 && < 5,
9499 base-compat >= 0.9.1 && < 0.10,
95− bytestring >= 0.10.4.0,
96100 containers >= 0.2.4.1,
97101 deepseq >= 1.3,
98102 dlist >= 0.2,
99103 ghc-prim >= 0.2,
100104 hashable >= 1.1.2.0,
101− scientific >= 0.3.1 && < 0.4,
105+ scientific >= 0.3.4.7 && < 0.4,
102106 tagged >=0.8.3 && <0.9,
103107 template-haskell >= 2.7,
104108 text >= 1.1.1.0,
@@ -107,10 +111,16 @@ library
107111 unordered-containers >= 0.2.5.0,
108112 vector >= 0.8
109113
114+ if flag(bytestring-builder)
115+ build-depends: bytestring >= 0.9 && < 0.10.4,
116+ bytestring-builder >= 0.10.4 && < 1
117+ else
118+ build-depends: bytestring >= 0.10.4
119+
110120 if !impl(ghc >= 8.0)
111121 -- `Data.Semigroup` and `Control.Monad.Fail` and `Control.Monad.IO.Class` are available in base only since GHC 8.0 / base 4.9
112122 build-depends:
113− semigroups >= 0.16.1 && < 0.19,
123+ semigroups >= 0.18.2 && < 0.19,
114124 transformers >= 0.2.2.0,
115125 transformers-compat >= 0.3,
116126 fail == 4.9.*
@@ -156,7 +166,6 @@ test-suite tests
156166 base,
157167 base-compat,
158168 base-orphans >= 0.5.3 && <0.6,
159− bytestring,
160169 containers,
161170 dlist,
162171 generic-deriving >= 1.10 && < 1.11,
@@ -175,9 +184,15 @@ test-suite tests
175184 vector,
176185 quickcheck-instances >=0.3.12
177186
187+ if flag(bytestring-builder)
188+ build-depends: bytestring >= 0.9 && < 0.10.4,
189+ bytestring-builder >= 0.10.4 && < 1
190+ else
191+ build-depends: bytestring >= 0.10.4
192+
178193 if !impl(ghc >= 8.0)
179194 build-depends:
180− semigroups >= 0.16.1 && < 0.19,
195+ semigroups >= 0.18.2 && < 0.19,
181196 transformers >= 0.2.2.0,
182197 transformers-compat >= 0.3
183198
benchmarks/aeson-benchmarks.cabal+19−4
@@ -4,6 +4,11 @@ build-type: Simple
44
55cabal-version: >=1.8
66
7+flag bytestring-builder
8+ description: Depend on the bytestring-builder package for backwards compatibility.
9+ default: False
10+ manual: False
11+
712library
813 hs-source-dirs: .. .
914
@@ -34,7 +39,6 @@ library
3439 base == 4.*,
3540 base-compat >= 0.9.1 && <0.10,
3641 time-locale-compat >=0.1.1 && <0.2,
37− bytestring >= 0.10.4.0,
3842 containers,
3943 deepseq,
4044 dlist >= 0.2,
@@ -42,7 +46,7 @@ library
4246 ghc-prim >= 0.2,
4347 hashable >= 1.1.2.0,
4448 mtl,
45− scientific >= 0.3.1 && < 0.4,
49+ scientific >= 0.3.4.7 && < 0.4,
4650 syb,
4751 tagged >=0.8.3 && <0.9,
4852 template-haskell >= 2.4,
@@ -52,12 +56,18 @@ library
5256 unordered-containers >= 0.2.3.0,
5357 vector >= 0.7.1
5458
59+ if flag(bytestring-builder)
60+ build-depends: bytestring >= 0.9 && < 0.10.4,
61+ bytestring-builder >= 0.10.4 && < 1
62+ else
63+ build-depends: bytestring >= 0.10.4
64+
5565 if impl(ghc >=7.8)
5666 cpp-options: -DHAS_COERCIBLE
5767
5868 if !impl(ghc >= 8.0)
5969 -- `Data.Semigroup` is available in base only since GHC 8.0 / base 4.9
60− build-depends: semigroups >= 0.16.1 && < 0.19
70+ build-depends: semigroups >= 0.18.2 && < 0.19
6171
6272 include-dirs: ../include
6373
@@ -99,13 +109,18 @@ executable aeson-benchmark-typed
99109 build-depends:
100110 aeson-benchmarks,
101111 base,
102− bytestring,
103112 criterion >= 1.0,
104113 deepseq,
105114 ghc-prim,
106115 text,
107116 time
108117
118+ if flag(bytestring-builder)
119+ build-depends: bytestring >= 0.9 && < 0.10.4,
120+ bytestring-builder >= 0.10.4 && < 1
121+ else
122+ build-depends: bytestring >= 0.10.4
123+
109124executable aeson-benchmark-compare-with-json
110125 main-is: CompareWithJSON.hs
111126 ghc-options: -Wall -O2 -rtsopts
stack.yaml+5−3
@@ -1,7 +1,9 @@
1−resolver: lts-5.16
1+resolver: lts-6.5
22packages:
33- '.'
44- benchmarks
55extra-deps:
6−- generic-deriving-1.10.4.1
7−flags: {}
6+- semigroups-0.18.2
7+flags:
8+ semigroups:
9+ bytestring-builder: false