@@ -81,13 +81,11 @@ import Data.ByteString.Internal as B hiding (c2w)
82#if MIN_VERSION_bytestring(0,10,4)
82#if MIN_VERSION_bytestring(0,10,4)
83import qualified Data.ByteString.Builder as B
83import qualified Data.ByteString.Builder as B
84+import qualified Data.ByteString.Builder.Extra as B
84import qualified Data.ByteString.Builder.Internal as B hiding (empty)
85import qualified Data.ByteString.Builder.Internal as B hiding (empty)
85import qualified Data.ByteString.Builder.Prim as BP
86import qualified Data.ByteString.Builder.Prim as BP
86import qualified Data.ByteString.Builder.Prim.Internal as BP
87import qualified Data.ByteString.Builder.Prim.Internal as BP
87import qualified Data.ByteString.Lazy as BL
88import qualified Data.ByteString.Lazy as BL
88−import Data.Text.Internal.Unsafe.Shift (shiftR)
90−import Data.Text.Internal.Unsafe.Shift (shiftL, shiftR)
93#if __GLASGOW_HASKELL__ >= 706
91#if __GLASGOW_HASKELL__ >= 706
@@ -99,6 +97,7 @@ import Foreign.ForeignPtr (unsafeForeignPtrToPtr)
99import Data.Text ()
97import Data.Text ()
100import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode)
98import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode)
101import Data.Text.Internal (Text(..), safe, textP)
99import Data.Text.Internal (Text(..), safe, textP)
100+import Data.Text.Internal.Unsafe.Shift (shiftL, shiftR)
102import Data.Text.Internal.Private (runText)
101import Data.Text.Internal.Private (runText)
103import Data.Text.Internal.Unsafe.Char (ord, unsafeWrite)
102import Data.Text.Internal.Unsafe.Char (ord, unsafeWrite)
104import Data.Word (Word8, Word32)
103import Data.Word (Word8, Word32)
@@ -322,7 +321,17 @@ decodeUtf8' = unsafeDupablePerformIO . try . evaluate . decodeUtf8With strictDec
322encodeUtf8 :: Text -> ByteString
321encodeUtf8 :: Text -> ByteString
323#if MIN_VERSION_bytestring(0,10,4)
322#if MIN_VERSION_bytestring(0,10,4)
324+encodeUtf8 t@(Text _arr _off len) =
325+ B.copy -- copy to trim and avoid wasting memory
327+ $ B.toLazyByteStringWith strategy BL.empty
325−encodeUtf8 = BL.toStrict . B.toLazyByteString . encodeUtf8Builder
328+ $ encodeUtf8Builder t
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
327-- | Encode text to a ByteString 'B.Builder' using UTF-8 encoding.
336-- | Encode text to a ByteString 'B.Builder' using UTF-8 encoding.
328encodeUtf8Builder :: Text -> B.Builder
337encodeUtf8Builder :: Text -> B.Builder
@@ -351,6 +360,8 @@ encodeUtf8BuilderEscaped be =
351 outerLoop !i0 !br@(B.BufferRange op0 ope)
360 outerLoop !i0 !br@(B.BufferRange op0 ope)
352 | i0 >= iend = k br
361 | i0 >= iend = k br
353 | outRemaining > 0 = goPartial (i0 + min outRemaining inpRemaining)
362 | 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.
354 | otherwise = return $ B.bufferFull bound op0 (outerLoop i0)
365 | otherwise = return $ B.bufferFull bound op0 (outerLoop i0)
356 outRemaining = (ope `minusPtr` op0) `div` bound
367 outRemaining = (ope `minusPtr` op0) `div` bound
@@ -387,6 +398,8 @@ encodeUtf8BuilderEscaped be =
388encodeUtf8 = encodeUtf8_0
399encodeUtf8 = encodeUtf8_0
390encodeUtf8_0 :: Text -> ByteString
403encodeUtf8_0 :: Text -> ByteString
391encodeUtf8_0 (Text arr off len) = unsafeDupablePerformIO $ do
404encodeUtf8_0 (Text arr off len) = unsafeDupablePerformIO $ do
392 let size0 = max len 4
405 let size0 = max len 4
@@ -431,8 +444,6 @@ encodeUtf8_0 (Text arr off len) = unsafeDupablePerformIO $ do
431 poke8 (m+2) $ (w .&. 0x3F) + 0x80
444 poke8 (m+2) $ (w .&. 0x3F) + 0x80
432 go (n+1) (m+3)
445 go (n+1) (m+3)
436encodeUtf8_1 :: Text -> ByteString
447encodeUtf8_1 :: Text -> ByteString
437encodeUtf8_1 (Text arr off len)
448encodeUtf8_1 (Text arr off len)
438 | len == 0 = B.empty
449 | len == 0 = B.empty