@@ -54,6 +54,14 @@ module Data.Text.Encoding
54 , encodeUtf16BE
54 , encodeUtf16BE
55 , encodeUtf32LE
55 , encodeUtf32LE
56 , encodeUtf32BE
56 , encodeUtf32BE
58+#if MIN_VERSION_bytestring(0,10,4)
59+ -- * Generic encoding of Text
60+ -- , encodeStreamWithB
62+ -- , encodeUtf8Builder
59import Control.Exception (evaluate, try)
67import Control.Exception (evaluate, try)
@@ -66,6 +74,13 @@ import Control.Monad.ST (runST)
66import Data.Bits ((.&.))
74import Data.Bits ((.&.))
67import Data.ByteString as B
75import Data.ByteString as B
68import Data.ByteString.Internal as B
76import Data.ByteString.Internal as B
77+#if MIN_VERSION_bytestring(0,10,4)
78+import qualified Data.ByteString.Builder as B
79+import qualified Data.ByteString.Builder.Internal as B
80+import qualified Data.ByteString.Builder.Prim as BP
81+import qualified Data.ByteString.Builder.Prim.Internal as BP
82+import qualified Data.ByteString.Lazy as BL
69import Data.Text ()
84import Data.Text ()
70import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode)
85import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode)
71import Data.Text.Internal (Text(..), safe, textP)
86import Data.Text.Internal (Text(..), safe, textP)
@@ -291,6 +306,61 @@ decodeUtf8' = unsafeDupablePerformIO . try . evaluate . decodeUtf8With strictDec
292-- | Encode text using UTF-8 encoding.
307-- | Encode text using UTF-8 encoding.
293encodeUtf8 :: Text -> ByteString
308encodeUtf8 :: Text -> ByteString
309+#if MIN_VERSION_bytestring(0,10,4)
312+ BL.toStrict . B.toLazyByteString
313+ . encodeUtf8Escaped (BP.liftFixedToBounded BP.word8)
315+-- | 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) =
321+ bound = max 4 $ BP.sizeBound be
326+ outerLoop !i0 !br@(B.BufferRange op0 ope)
328+ | op0 `plusPtr` bound < ope =
329+ goPartial (i0 + min outRemaining inpRemaining)
330+ | otherwise = return $ B.bufferFull bound op0 (outerLoop i0)
332+ outRemaining = (ope `minusPtr` op0) `div` bound
333+ inpRemaining = iend - i0
335+ goPartial !iendTmp = go i0 op0
338+ | i < iendTmp = case A.unsafeIndex arr i of
340+ BP.runB be (fromIntegral w) op >>= go (i + 1)
342+ poke8 0 $ (w `shiftR` 6) + 0xC0
343+ poke8 1 $ (w .&. 0x3f) + 0x80
344+ go (i + 1) (op `plusPtr` 2)
345+ | 0xD800 <= w && w <= 0xDBFF -> do
346+ let c = ord $ U16.chr2 w (A.unsafeIndex arr (i+1))
347+ poke8 0 $ (c `shiftR` 18) + 0xF0
348+ poke8 1 $ ((c `shiftR` 12) .&. 0x3F) + 0x80
349+ poke8 2 $ ((c `shiftR` 6) .&. 0x3F) + 0x80
350+ poke8 3 $ (c .&. 0x3F) + 0x80
351+ go (i + 2) (op `plusPtr` 4)
353+ poke8 0 $ (w `shiftR` 12) + 0xE0
354+ poke8 1 $ ((w `shiftR` 6) .&. 0x3F) + 0x80
355+ poke8 2 $ (w .&. 0x3F) + 0x80
356+ go (i + 1) (op `plusPtr` 3)
358+ outerLoop i (B.BufferRange op ope)
360+ poke8 j v = poke (op `plusPtr` j) (fromIntegral v :: Word8)
294encodeUtf8 (Text arr off len) = unsafeDupablePerformIO $ do
364encodeUtf8 (Text arr off len) = unsafeDupablePerformIO $ do
295 let size0 = max len 4
365 let size0 = max len 4
296 mallocByteString size0 >>= start size0 off 0
366 mallocByteString size0 >>= start size0 off 0
@@ -344,6 +414,7 @@ encodeUtf8 (Text arr off len) = unsafeDupablePerformIO $ do
344 poke8 (m+1) $ ((w `shiftR` 6) .&. 0x3F) + 0x80
414 poke8 (m+1) $ ((w `shiftR` 6) .&. 0x3F) + 0x80
345 poke8 (m+2) $ (w .&. 0x3F) + 0x80
415 poke8 (m+2) $ (w .&. 0x3F) + 0x80
346 go (n+1) (m+3)
416 go (n+1) (m+3)
348-- | Decode text from little endian UTF-16 encoding.
419-- | Decode text from little endian UTF-16 encoding.
349decodeUtf16LEWith :: OnDecodeError -> ByteString -> Text
420decodeUtf16LEWith :: OnDecodeError -> ByteString -> Text
@@ -432,3 +503,36 @@ foreign import ccall unsafe "_hs_text_decode_utf8_state" c_decode_utf8_with_stat
433foreign import ccall unsafe "_hs_text_decode_latin1" c_decode_latin1
504foreign import ccall unsafe "_hs_text_decode_latin1" c_decode_latin1
434 :: MutableByteArray# s -> Ptr Word8 -> Ptr Word8 -> IO ()
505 :: MutableByteArray# s -> Ptr Word8 -> Ptr Word8 -> IO ()
509+-- | Encode all elements of a 'F.Stream' using a 'B.BoundedEncoding'.
510+{-# INLINE encodeStreamWithB #-}
511+encodeStreamWithB :: B.BoundedEncoding a -> F.Stream a -> B.Builder
512+encodeStreamWithB be =
513+ \(F.Stream next s0 _) -> B.builder $ step next s0
515+ bound = B.sizeBound be
516+ step next s0 k (B.BufferRange op0 ope0) =
519+ go s !op = case next s of
520+ F.Done -> k (B.BufferRange op ope0)
521+ F.Skip s' -> go s' op
523+ | op `plusPtr` bound <= ope0 -> B.runB be x op >>= go s'
525+ return $ B.bufferFull bound op (step next s k)
529+-- | /Subject to fusion./
530+-- Encode all 'Char's of a 'T.Text' using a 'B.BoundedEncoding'.
531+{-# INLINE encodeTextWithB #-}
532+encodeTextWithB :: B.BoundedEncoding Char -> Text -> B.Builder
533+encodeTextWithB be = encodeStreamWithB be . F.stream
535+-- | Encode text using UTF-8 encoding.
536+encodeUtf8Builder :: Text -> B.Builder
537+encodeUtf8Builder = encodeUtf8Escaped (B.fromF B.word8)