@@ -56,37 +56,44 @@ module Data.Text.Encoding
5656 , encodeUtf32BE
5757
5858#if MIN_VERSION_bytestring(0,10,4)
59− -- * Generic encoding of Text
60− -- , encodeStreamWithB
61− -- , encodeTextWithB
62− -- , encodeUtf8Builder
63− , encodeUtf8Escaped
59+ -- * Encoding Text using ByteString Builders
60+ -- | /Note/ that these functions are only available if built against
61+ -- @bytestring >= 0.10.4.0@.
62+ , encodeUtf8Builder
63+ , encodeUtf8BuilderEscaped
6464#endif
6565 ) where
6666
6767import Control.Exception (evaluate, try)
68+
6869#if __GLASGOW_HASKELL__ >= 702
6970import Control.Monad.ST.Unsafe (unsafeIOToST, unsafeSTToIO)
7071#else
7172import Control.Monad.ST (unsafeIOToST, unsafeSTToIO)
7273#endif
74+
7375import Control.Monad.ST (runST)
7476import Data.Bits ((.&.))
7577import Data.ByteString as B
76−import Data.ByteString.Internal as B
78+import Data.ByteString.Internal as B hiding (c2w)
79+
7780#if MIN_VERSION_bytestring(0,10,4)
81+import Data.Monoid ((<>))
7882import qualified Data.ByteString.Builder as B
7983import qualified Data.ByteString.Builder.Internal as B
8084import qualified Data.ByteString.Builder.Prim as BP
8185import qualified Data.ByteString.Builder.Prim.Internal as BP
8286import qualified Data.ByteString.Lazy as BL
87+import Data.Text.Internal.Unsafe.Shift (shiftR)
88+#else
89+import Data.Text.Internal.Unsafe.Shift (shiftL, shiftR)
8390#endif
91+
8492import Data.Text ()
8593import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode)
8694import Data.Text.Internal (Text(..), safe, textP)
8795import Data.Text.Internal.Private (runText)
8896import Data.Text.Internal.Unsafe.Char (ord, unsafeWrite)
89−import Data.Text.Internal.Unsafe.Shift (shiftL, shiftR)
9097import Data.Word (Word8, Word32)
9198import Foreign.C.Types (CSize)
9299import Foreign.ForeignPtr (withForeignPtr)
@@ -308,26 +315,36 @@ decodeUtf8' = unsafeDupablePerformIO . try . evaluate . decodeUtf8With strictDec
308315encodeUtf8 :: Text -> ByteString
309316#if MIN_VERSION_bytestring(0,10,4)
310317
311−encodeUtf8 =
312− BL.toStrict . B.toLazyByteString
313− . encodeUtf8Escaped (BP.liftFixedToBounded BP.word8)
318+encodeUtf8 = BL.toStrict . B.toLazyByteString . encodeUtf8Builder
319+
320+-- | Encode text to a ByteString 'B.Builder' using UTF-8 encoding.
321+encodeUtf8Builder :: Text -> B.Builder
322+encodeUtf8Builder = encodeUtf8BuilderEscaped (BP.liftFixedToBounded BP.word8)
314323
315324-- | Encode text using UTF-8 encoding and escape the ASCII characters using
316−-- a 'BP.PrimBounded'.
317−encodeUtf8Escaped :: BP.BoundedPrim Word8 -> Text -> B.Builder
318−encodeUtf8Escaped be (Text arr off len) =
319− B.builder step
325+-- a 'BP.BoundedPrim'.
326+--
327+-- Use this function is to implement efficient encoders for text-based formats
328+-- like JSON or HTML.
329+{-# INLINE encodeUtf8BuilderEscaped #-}
330+-- TODO: Extend documentation with references to source code in @blaze-html@
331+-- or @aeson@ that uses this function.
332+encodeUtf8BuilderEscaped :: BP.BoundedPrim Word8 -> Text -> B.Builder
333+encodeUtf8BuilderEscaped be =
334+ -- manual eta-expansion to ensure inlining works as expected
335+ \txt -> B.builder (mkBuildstep txt)
320336 where
321− bound = max 4 $ BP.sizeBound be
322− iend = off + len
323− step !k =
337+ bound = max 4 $ BP.sizeBound be
338+
339+ mkBuildstep (Text arr off len) !k =
324340 outerLoop off
325341 where
342+ iend = off + len
343+
326344 outerLoop !i0 !br@(B.BufferRange op0 ope)
327− | i0 >= iend = k br
328− | op0 `plusPtr` bound < ope =
329− goPartial (i0 + min outRemaining inpRemaining)
330− | otherwise = return $ B.bufferFull bound op0 (outerLoop i0)
345+ | i0 >= iend = k br
346+ | outRemaining > 0 = goPartial (i0 + min outRemaining inpRemaining)
347+ | otherwise = return $ B.bufferFull bound op0 (outerLoop i0)
331348 where
332349 outRemaining = (ope `minusPtr` op0) `div` bound
333350 inpRemaining = iend - i0
@@ -492,36 +509,3 @@ foreign import ccall unsafe "_hs_text_decode_utf8_state" c_decode_utf8_with_stat
492509
493510foreign import ccall unsafe "_hs_text_decode_latin1" c_decode_latin1
494511 :: MutableByteArray# s -> Ptr Word8 -> Ptr Word8 -> IO ()
495−
496−
497−{-
498−-- | Encode all elements of a 'F.Stream' using a 'B.BoundedEncoding'.
499−{-# INLINE encodeStreamWithB #-}
500−encodeStreamWithB :: B.BoundedEncoding a -> F.Stream a -> B.Builder
501−encodeStreamWithB be =
502− \(F.Stream next s0 _) -> B.builder $ step next s0
503− where
504− bound = B.sizeBound be
505− step next s0 k (B.BufferRange op0 ope0) =
506− go s0 op0
507− where
508− go s !op = case next s of
509− F.Done -> k (B.BufferRange op ope0)
510− F.Skip s' -> go s' op
511− F.Yield x s'
512− | op `plusPtr` bound <= ope0 -> B.runB be x op >>= go s'
513− | otherwise ->
514− return $ B.bufferFull bound op (step next s k)
515−
516−
517−-- |
518−-- | /Subject to fusion./
519−-- Encode all 'Char's of a 'T.Text' using a 'B.BoundedEncoding'.
520−{-# INLINE encodeTextWithB #-}
521−encodeTextWithB :: B.BoundedEncoding Char -> Text -> B.Builder
522−encodeTextWithB be = encodeStreamWithB be . F.stream
523−
524−-- | Encode text using UTF-8 encoding.
525−encodeUtf8Builder :: Text -> B.Builder
526−encodeUtf8Builder = encodeUtf8Escaped (B.fromF B.word8)
527−-}