@@ -1,3 +1,4 @@
1+{-# LANGUAGE BangPatterns #-}
2-- Module : Data.Text.Lazy.Encoding
3-- Module : Data.Text.Lazy.Encoding
3-- Copyright : (c) 2009, 2010 Bryan O'Sullivan
4-- Copyright : (c) 2009, 2010 Bryan O'Sullivan
@@ -19,12 +20,14 @@ module Data.Text.Lazy.Encoding
19 -- * Decoding ByteStrings to Text
20 -- * Decoding ByteStrings to Text
20 decodeASCII
21 decodeASCII
21 , decodeUtf8
22 , decodeUtf8
22 , decodeUtf16LE
24 , decodeUtf16LE
23 , decodeUtf16BE
25 , decodeUtf16BE
24 , decodeUtf32LE
26 , decodeUtf32LE
25 , decodeUtf32BE
27 , decodeUtf32BE
26 -- ** Controllable error handling
28 -- ** Controllable error handling
27 , decodeUtf8With
29 , decodeUtf8With
28 , decodeUtf16LEWith
31 , decodeUtf16LEWith
29 , decodeUtf16BEWith
32 , decodeUtf16BEWith
30 , decodeUtf32LEWith
33 , decodeUtf32LEWith
@@ -38,17 +41,21 @@ module Data.Text.Lazy.Encoding
38 , encodeUtf32BE
41 , encodeUtf32BE
44+import Data.Bits ((.&.))
41import Data.Text.Encoding.Error (OnDecodeError, strictDecode)
45import Data.Text.Encoding.Error (OnDecodeError, strictDecode)
42−import Data.Text.Lazy.Internal (Text(..), chunk, foldrChunks)
46+import Data.Text.Lazy.Internal (Text(..), chunk, empty, foldrChunks)
47+import qualified Data.ByteString as S
43import qualified Data.ByteString.Lazy as B
48import qualified Data.ByteString.Lazy as B
44import qualified Data.ByteString.Lazy.Internal as B
49import qualified Data.ByteString.Lazy.Internal as B
50+import qualified Data.ByteString.Unsafe as S
51+import qualified Data.Text as T
45import qualified Data.Text.Encoding as TE
52import qualified Data.Text.Encoding as TE
46import qualified Data.Text.Lazy.Encoding.Fusion as E
53import qualified Data.Text.Lazy.Encoding.Fusion as E
47import qualified Data.Text.Lazy.Fusion as F
54import qualified Data.Text.Lazy.Fusion as F
49-- | Decode a 'ByteString' containing 7-bit ASCII encoded text.
56-- | Decode a 'ByteString' containing 7-bit ASCII encoded text.
50decodeASCII :: B.ByteString -> Text
57decodeASCII :: B.ByteString -> Text
51−decodeASCII bs = foldr (chunk . TE.decodeASCII) Empty (B.toChunks bs)
58+decodeASCII bs = foldr (chunk . TE.decodeASCII) empty (B.toChunks bs)
52{-# INLINE decodeASCII #-}
59{-# INLINE decodeASCII #-}
54-- | Decode a 'ByteString' containing UTF-8 encoded text.
61-- | Decode a 'ByteString' containing UTF-8 encoded text.
@@ -61,6 +68,53 @@ decodeUtf8 :: B.ByteString -> Text
61decodeUtf8 = decodeUtf8With strictDecode
68decodeUtf8 = decodeUtf8With strictDecode
62{-# INLINE decodeUtf8 #-}
69{-# INLINE decodeUtf8 #-}
71+-- | Decode a 'ByteString' containing UTF-8 encoded text.
72+decodeUtf8With' :: OnDecodeError -> B.ByteString -> Text
73+decodeUtf8With' onErr bs0 = fast bs0
75+ decode = TE.decodeUtf8With onErr
76+ fast (B.Chunk p ps) | isComplete p = chunk (decode p) (fast ps)
77+ | otherwise = chunk (decode h) (slow t ps)
78+ where (h,t) = S.splitAt pivot p
83+ at n = len >= n && S.unsafeIndex p (len-n) .&. 0xc0 == 0xc0
85+ slow i bs = {-# SCC "decodeUtf8With'/slow" #-}
87+ Just (w,bs') | isComplete i' -> chunk (decode i') (fast bs')
88+ | otherwise -> slow i' bs'
89+ where i' = S.snoc i w
90+ Nothing -> case S.uncons i of
92+ case onErr desc (Just j) of
93+ Nothing -> slow i' bs
94+ Just c -> Chunk (T.singleton c) (slow i' bs)
96+ case onErr desc Nothing of
98+ Just c -> Chunk (T.singleton c) empty
99+ isComplete bs = {-# SCC "decodeUtf8With'/isComplete" #-}
100+ ix 1 .&. 0x80 == 0 ||
101+ (len >= 2 && ix 2 .&. 0xe0 == 0xc0) ||
102+ (len >= 3 && ix 3 .&. 0xf0 == 0xe0) ||
103+ (len >= 4 && ix 4 .&. 0xf8 == 0xf0)
104+ where len = S.length bs
105+ ix n = S.unsafeIndex bs (len-n)
106+ desc = "Data.Text.Lazy.Encoding.decodeUtf8With: Invalid UTF-8 stream"
107+{-# INLINE[0] decodeUtf8With' #-}
109+-- | Decode a 'ByteString' containing UTF-8 encoded text.
110+decodeUtf8' :: B.ByteString -> Text
111+decodeUtf8' = decodeUtf8With' strictDecode
112+{-# INLINE[0] decodeUtf8' #-}
114+-- This rule seems to cause performance loss.
115+{- RULES "LAZY STREAM stream/decodeUtf8' fusion" [1]
116+ forall bs. F.stream (decodeUtf8' bs) = E.streamUtf8 strictDecode bs #-}
64encodeUtf8 :: Text -> B.ByteString
118encodeUtf8 :: Text -> B.ByteString
65encodeUtf8 (Chunk c cs) = B.Chunk (TE.encodeUtf8 c) (encodeUtf8 cs)
119encodeUtf8 (Chunk c cs) = B.Chunk (TE.encodeUtf8 c) (encodeUtf8 cs)
66encodeUtf8 Empty = B.Empty
120encodeUtf8 Empty = B.Empty