@@ -81,13 +81,11 @@ import Data.ByteString.Internal as B hiding (c2w)
8181
8282#if MIN_VERSION_bytestring(0,10,4)
8383import qualified Data.ByteString.Builder as B
84+import qualified Data.ByteString.Builder.Extra as B
8485import qualified Data.ByteString.Builder.Internal as B hiding (empty)
8586import qualified Data.ByteString.Builder.Prim as BP
8687import qualified Data.ByteString.Builder.Prim.Internal as BP
8788import qualified Data.ByteString.Lazy as BL
88−import Data.Text.Internal.Unsafe.Shift (shiftR)
89−#else
90−import Data.Text.Internal.Unsafe.Shift (shiftL, shiftR)
9189#endif
9290
9391#if __GLASGOW_HASKELL__ >= 706
@@ -99,6 +97,7 @@ import Foreign.ForeignPtr (unsafeForeignPtrToPtr)
9997import Data.Text ()
10098import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode)
10199import Data.Text.Internal (Text(..), safe, textP)
100+import Data.Text.Internal.Unsafe.Shift (shiftL, shiftR)
102101import Data.Text.Internal.Private (runText)
103102import Data.Text.Internal.Unsafe.Char (ord, unsafeWrite)
104103import Data.Word (Word8, Word32)
@@ -322,7 +321,17 @@ decodeUtf8' = unsafeDupablePerformIO . try . evaluate . decodeUtf8With strictDec
322321encodeUtf8 :: Text -> ByteString
323322#if MIN_VERSION_bytestring(0,10,4)
324323
325−encodeUtf8 = BL.toStrict . B.toLazyByteString . encodeUtf8Builder
324+encodeUtf8 t@(Text _arr _off len) =
325+ B.copy -- copy to trim and avoid wasting memory
326+ $ BL.toStrict
327+ $ B.toLazyByteStringWith strategy BL.empty
328+ $ encodeUtf8Builder t
329+ where
330+ -- We use a strategy that allocates a buffer that is guaranteed to be
331+ -- large enough for the whole result. This ensures that we always stay in
332+ -- the fast 'goPartial' loop in the 'encodeUtf8BuilderEscaped' function.
333+ strategy = B.untrimmedStrategy (4 * (len + 1)) B.defaultChunkSize
334+
326335
327336-- | Encode text to a ByteString 'B.Builder' using UTF-8 encoding.
328337encodeUtf8Builder :: Text -> B.Builder
@@ -351,6 +360,8 @@ encodeUtf8BuilderEscaped be =
351360 outerLoop !i0 !br@(B.BufferRange op0 ope)
352361 | i0 >= iend = k br
353362 | outRemaining > 0 = goPartial (i0 + min outRemaining inpRemaining)
363+ -- TODO: Use a loop with an integrated bound's check if outRemaining
364+ -- is smaller than 8, as this will save on divisions.
354365 | otherwise = return $ B.bufferFull bound op0 (outerLoop i0)
355366 where
356367 outRemaining = (ope `minusPtr` op0) `div` bound
@@ -387,6 +398,8 @@ encodeUtf8BuilderEscaped be =
387398
388399encodeUtf8 = encodeUtf8_0
389400
401+#endif
402+
390403encodeUtf8_0 :: Text -> ByteString
391404encodeUtf8_0 (Text arr off len) = unsafeDupablePerformIO $ do
392405 let size0 = max len 4
@@ -431,8 +444,6 @@ encodeUtf8_0 (Text arr off len) = unsafeDupablePerformIO $ do
431444 poke8 (m+2) $ (w .&. 0x3F) + 0x80
432445 go (n+1) (m+3)
433446
434−#endif
435−
436447encodeUtf8_1 :: Text -> ByteString
437448encodeUtf8_1 (Text arr off len)
438449 | len == 0 = B.empty