Merge branch 'master' into feature-new-bytestring-builder
[packages/text.git] / Data / Text / Lazy / Encoding.hs
index cb51b0b..1498f8d 100644 (file)
@@ -1,4 +1,7 @@
-{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE BangPatterns,CPP #-}
+#if __GLASGOW_HASKELL__ >= 702
+{-# LANGUAGE Trustworthy #-}
+#endif
 -- |
 -- Module      : Data.Text.Lazy.Encoding
 -- Copyright   : (c) 2009, 2010 Bryan O'Sullivan
@@ -20,11 +23,16 @@ module Data.Text.Lazy.Encoding
     -- * Decoding ByteStrings to Text
     -- $strict
       decodeASCII
+    , decodeLatin1
     , decodeUtf8
     , decodeUtf16LE
     , decodeUtf16BE
     , decodeUtf32LE
     , decodeUtf32BE
+
+    -- ** Catchable failure
+    , decodeUtf8'
+
     -- ** Controllable error handling
     , decodeUtf8With
     , decodeUtf16LEWith
@@ -40,17 +48,22 @@ module Data.Text.Lazy.Encoding
     , encodeUtf32BE
     ) where
 
+import Control.Exception (evaluate, try)
 import Data.Bits ((.&.))
-import Data.Text.Encoding.Error (OnDecodeError, strictDecode)
+import Data.Monoid (mempty, (<>))
+import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode)
 import Data.Text.Lazy.Internal (Text(..), chunk, empty, foldrChunks)
 import qualified Data.ByteString as S
 import qualified Data.ByteString.Lazy as B
 import qualified Data.ByteString.Lazy.Internal as B
 import qualified Data.ByteString.Unsafe as S
+import qualified Data.ByteString.Builder               as B
+import qualified Data.ByteString.Builder.Prim          as BP
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as TE
 import qualified Data.Text.Lazy.Encoding.Fusion as E
 import qualified Data.Text.Lazy.Fusion as F
+import Data.Text.Unsafe (unsafeDupablePerformIO)
 
 -- $strict
 --
@@ -65,10 +78,17 @@ import qualified Data.Text.Lazy.Fusion as F
 -- 'decodeUtf8With' allows the programmer to determine what to do on a
 -- decoding error.
 
--- | Decode a 'ByteString' containing 7-bit ASCII encoded text.
+-- | /Deprecated/.  Decode a 'ByteString' containing 7-bit ASCII
+-- encoded text.
+--
+-- This function is deprecated.  Use 'decodeLatin1' instead.
 decodeASCII :: B.ByteString -> Text
-decodeASCII bs = foldr (chunk . TE.decodeASCII) empty (B.toChunks bs)
-{-# INLINE decodeASCII #-}
+decodeASCII = decodeUtf8
+{-# DEPRECATED decodeASCII "Use decodeUtf8 instead" #-}
+
+-- | Decode a 'ByteString' containing Latin-1 (aka ISO-8859-1) encoded text.
+decodeLatin1 :: B.ByteString -> Text
+decodeLatin1 = foldr (chunk . TE.decodeLatin1) empty . B.toChunks
 
 -- | Decode a 'ByteString' containing UTF-8 encoded text.
 decodeUtf8With :: OnDecodeError -> B.ByteString -> Text
@@ -108,10 +128,12 @@ decodeUtf8With onErr bs0 = fast bs0
     desc = "Data.Text.Lazy.Encoding.decodeUtf8With: Invalid UTF-8 stream"
 {-# INLINE[0] decodeUtf8With #-}
 
--- | Decode a 'ByteString' containing UTF-8 encoded text.
+-- | Decode a 'ByteString' containing UTF-8 encoded text that is known
+-- to be valid.
 --
 -- If the input contains any invalid UTF-8 data, an exception will be
--- thrown.  For more control over the handling of invalid data, use
+-- thrown that cannot be caught in pure code.  For more control over
+-- the handling of invalid data, use 'decodeUtf8'' or
 -- 'decodeUtf8With'.
 decodeUtf8 :: B.ByteString -> Text
 decodeUtf8 = decodeUtf8With strictDecode
@@ -121,9 +143,31 @@ decodeUtf8 = decodeUtf8With strictDecode
 {- RULES "LAZY STREAM stream/decodeUtf8' fusion" [1]
    forall bs. F.stream (decodeUtf8' bs) = E.streamUtf8 strictDecode bs #-}
 
+-- | Decode a 'ByteString' containing UTF-8 encoded text..
+--
+-- If the input contains any invalid UTF-8 data, the relevant
+-- exception will be returned, otherwise the decoded text.
+--
+-- /Note/: this function is /not/ lazy, as it must decode its entire
+-- input before it can return a result.  If you need lazy (streaming)
+-- decoding, use 'decodeUtf8With' in lenient mode.
+decodeUtf8' :: B.ByteString -> Either UnicodeException Text
+decodeUtf8' bs = unsafeDupablePerformIO $ do
+                   let t = decodeUtf8 bs
+                   try (evaluate (rnf t `seq` t))
+  where
+    rnf Empty        = ()
+    rnf (Chunk _ ts) = rnf ts
+{-# INLINE decodeUtf8' #-}
+
 encodeUtf8 :: Text -> B.ByteString
-encodeUtf8 (Chunk c cs) = B.Chunk (TE.encodeUtf8 c) (encodeUtf8 cs)
-encodeUtf8 Empty        = B.Empty
+encodeUtf8 =
+    B.toLazyByteString . go
+  where
+    go Empty        = mempty
+    go (Chunk c cs) =
+        TE.encodeUtf8Escaped (BP.liftFixedToBounded BP.word8) c <> go cs
+
 
 -- | Decode text from little endian UTF-16 encoding.
 decodeUtf16LEWith :: OnDecodeError -> B.ByteString -> Text