on master help sign in
Use streaming UTF-8 decode to implement lazy decode
77825a2Bryan O'Sullivan
Data/Text/Lazy/Encoding.hs+14−36
@@ -49,15 +49,14 @@ module Data.Text.Lazy.Encoding
49 ) where
49 ) where
50
50
51import Control.Exception (evaluate, try)
51import Control.Exception (evaluate, try)
52−import Data.Bits ((.&.))
53import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode)
52import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode)
54import Data.Text.Lazy.Internal (Text(..), chunk, empty, foldrChunks)
53import Data.Text.Lazy.Internal (Text(..), chunk, empty, foldrChunks)
55import qualified Data.ByteString as S
54import qualified Data.ByteString as S
56import qualified Data.ByteString.Lazy as B
55import qualified Data.ByteString.Lazy as B
57import qualified Data.ByteString.Lazy.Internal as B
56import qualified Data.ByteString.Lazy.Internal as B
58−import qualified Data.ByteString.Unsafe as S
57+import qualified Data.ByteString.Unsafe as B
59−import qualified Data.Text as T
60import qualified Data.Text.Encoding as TE
58import qualified Data.Text.Encoding as TE
59+import qualified Data.Text.Lazy as L
61import qualified Data.Text.Lazy.Encoding.Fusion as E
60import qualified Data.Text.Lazy.Encoding.Fusion as E
62import qualified Data.Text.Lazy.Fusion as F
61import qualified Data.Text.Lazy.Fusion as F
63import Data.Text.Unsafe (unsafeDupablePerformIO)
62import Data.Text.Unsafe (unsafeDupablePerformIO)
@@ -89,41 +88,20 @@ decodeLatin1 = foldr (chunk . TE.decodeLatin1) empty . B.toChunks
89
88
90-- | Decode a 'ByteString' containing UTF-8 encoded text.
89-- | Decode a 'ByteString' containing UTF-8 encoded text.
91decodeUtf8With :: OnDecodeError -> B.ByteString -> Text
90decodeUtf8With :: OnDecodeError -> B.ByteString -> Text
92−decodeUtf8With onErr bs0 = fast bs0
91+decodeUtf8With onErr (B.Chunk b0 bs0) =
92+ case TE.streamDecodeUtf8With onErr b0 of
93+ TE.Some t l f -> chunk t (go f l bs0)
93 where
94 where
94− decode = TE.decodeUtf8With onErr
95+ go f0 _ (B.Chunk b bs) =
95− fast (B.Chunk p ps) | isComplete p = chunk (decode p) (fast ps)
96+ case f0 b of
96− | otherwise = chunk (decode h) (slow t ps)
97+ TE.Some t l f -> chunk t (go f l bs)
97− where (h,t) = S.splitAt pivot p
98+ go _ l _
98− pivot | at 1 = len-1
99+ | S.null l = empty
99− | at 2 = len-2
100+ | otherwise = case onErr desc (Just (B.unsafeHead l)) of
100− | otherwise = len-3
101+ Nothing -> empty
101− len = S.length p
102+ Just c -> L.singleton c
102− at n = len >= n && S.unsafeIndex p (len-n) .&. 0xc0 == 0xc0
103− fast B.Empty = empty
104− slow i bs = {-# SCC "decodeUtf8With'/slow" #-}
105− case B.uncons bs of
106− Just (w,bs') | isComplete i' -> chunk (decode i') (fast bs')
107− | otherwise -> slow i' bs'
108− where i' = S.snoc i w
109− Nothing -> case S.uncons i of
110− Just (j,i') ->
111− case onErr desc (Just j) of
112− Nothing -> slow i' bs
113− Just c -> Chunk (T.singleton c) (slow i' bs)
114− Nothing ->
115− case onErr desc Nothing of
116− Nothing -> empty
117− Just c -> Chunk (T.singleton c) empty
118− isComplete bs = {-# SCC "decodeUtf8With'/isComplete" #-}
119− ix 1 .&. 0x80 == 0 ||
120− (len >= 2 && ix 2 .&. 0xe0 == 0xc0) ||
121− (len >= 3 && ix 3 .&. 0xf0 == 0xe0) ||
122− (len >= 4 && ix 4 .&. 0xf8 == 0xf0)
123− where len = S.length bs
124− ix n = S.unsafeIndex bs (len-n)
125 desc = "Data.Text.Lazy.Encoding.decodeUtf8With: Invalid UTF-8 stream"
103 desc = "Data.Text.Lazy.Encoding.decodeUtf8With: Invalid UTF-8 stream"
126−{-# INLINE[0] decodeUtf8With #-}
104+decodeUtf8With _ _ = empty
127
105
128-- | Decode a 'ByteString' containing UTF-8 encoded text that is known
106-- | Decode a 'ByteString' containing UTF-8 encoded text that is known
129-- to be valid.
107-- to be valid.