@@ -1,4 +1,4 @@
1−{-# LANGUAGE BangPatterns #-}
1+{-# LANGUAGE BangPatterns, Rank2Types #-}
22
33-- |
44-- Module : Data.Text.Encoding.Fusion
@@ -35,6 +35,7 @@ import Control.Exception (assert)
3535import Data.ByteString as B
3636import Data.ByteString.Internal (ByteString(..), mallocByteString, memcpy)
3737import Data.Text.Fusion (Step(..), Stream(..))
38+import Data.Text.Encoding.Error
3839import Data.Text.Encoding.Fusion.Common
3940import Data.Text.UnsafeChar (unsafeChr, unsafeChr8, unsafeChr32)
4041import Data.Text.UnsafeShift (shiftL)
@@ -62,8 +63,8 @@ streamASCII bs = Stream next 0 l
6263
6364-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using UTF-8
6465-- encoding.
65−streamUtf8 :: ByteString -> Stream Char
66−streamUtf8 bs = Stream next 0 l
66+streamUtf8 :: OnDecodeError -> ByteString -> Stream Char
67+streamUtf8 onErr bs = Stream next 0 l
6768 where
6869 l = B.length bs
6970 {-# INLINE next #-}
@@ -73,8 +74,9 @@ streamUtf8 bs = Stream next 0 l
7374 | i+1 < l && U8.validate2 x1 x2 = Yield (U8.chr2 x1 x2) (i+2)
7475 | i+2 < l && U8.validate3 x1 x2 x3 = Yield (U8.chr3 x1 x2 x3) (i+3)
7576 | i+3 < l && U8.validate4 x1 x2 x3 x4 = Yield (U8.chr4 x1 x2 x3 x4) (i+4)
76− | otherwise = encodingError "UTF-8"
77+ | otherwise = decodeError "streamUtf8" "UTF-8" onErr mx (i+1)
7778 where
79+ mx = if i >= l then Nothing else Just x1
7880 x1 = idx i
7981 x2 = idx (i + 1)
8082 x3 = idx (i + 2)
@@ -84,8 +86,8 @@ streamUtf8 bs = Stream next 0 l
8486
8587-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using little
8688-- endian UTF-16 encoding.
87−streamUtf16LE :: ByteString -> Stream Char
88−streamUtf16LE bs = Stream next 0 l
89+streamUtf16LE :: OnDecodeError -> ByteString -> Stream Char
90+streamUtf16LE onErr bs = Stream next 0 l
8991 where
9092 l = B.length bs
9193 {-# INLINE next #-}
@@ -93,7 +95,7 @@ streamUtf16LE bs = Stream next 0 l
9395 | i >= l = Done
9496 | i+1 < l && U16.validate1 x1 = Yield (unsafeChr x1) (i+2)
9597 | i+3 < l && U16.validate2 x1 x2 = Yield (U16.chr2 x1 x2) (i+4)
96− | otherwise = encodingError "UTF-16LE"
98+ | otherwise = decodeError "streamUtf16LE" "UTF-16LE" onErr Nothing (i+1)
9799 where
98100 x1 = idx i + (idx (i + 1) `shiftL` 8)
99101 x2 = idx (i + 2) + (idx (i + 3) `shiftL` 8)
@@ -102,8 +104,8 @@ streamUtf16LE bs = Stream next 0 l
102104
103105-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using big
104106-- endian UTF-16 encoding.
105−streamUtf16BE :: ByteString -> Stream Char
106−streamUtf16BE bs = Stream next 0 l
107+streamUtf16BE :: OnDecodeError -> ByteString -> Stream Char
108+streamUtf16BE onErr bs = Stream next 0 l
107109 where
108110 l = B.length bs
109111 {-# INLINE next #-}
@@ -111,7 +113,7 @@ streamUtf16BE bs = Stream next 0 l
111113 | i >= l = Done
112114 | i+1 < l && U16.validate1 x1 = Yield (unsafeChr x1) (i+2)
113115 | i+3 < l && U16.validate2 x1 x2 = Yield (U16.chr2 x1 x2) (i+4)
114− | otherwise = encodingError "UTF16-BE"
116+ | otherwise = decodeError "streamUtf16BE" "UTF-16BE" onErr Nothing (i+1)
115117 where
116118 x1 = (idx i `shiftL` 8) + idx (i + 1)
117119 x2 = (idx (i + 2) `shiftL` 8) + idx (i + 3)
@@ -120,15 +122,15 @@ streamUtf16BE bs = Stream next 0 l
120122
121123-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using big
122124-- endian UTF-32 encoding.
123−streamUtf32BE :: ByteString -> Stream Char
124−streamUtf32BE bs = Stream next 0 l
125+streamUtf32BE :: OnDecodeError -> ByteString -> Stream Char
126+streamUtf32BE onErr bs = Stream next 0 l
125127 where
126128 l = B.length bs
127129 {-# INLINE next #-}
128130 next i
129131 | i >= l = Done
130132 | i+3 < l && U32.validate x = Yield (unsafeChr32 x) (i+4)
131− | otherwise = encodingError "UTF-32BE"
133+ | otherwise = decodeError "streamUtf32BE" "UTF-32BE" onErr Nothing (i+1)
132134 where
133135 x = shiftL x1 24 + shiftL x2 16 + shiftL x3 8 + x4
134136 x1 = idx i
@@ -140,15 +142,15 @@ streamUtf32BE bs = Stream next 0 l
140142
141143-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using little
142144-- endian UTF-32 encoding.
143−streamUtf32LE :: ByteString -> Stream Char
144−streamUtf32LE bs = Stream next 0 l
145+streamUtf32LE :: OnDecodeError -> ByteString -> Stream Char
146+streamUtf32LE onErr bs = Stream next 0 l
145147 where
146148 l = B.length bs
147149 {-# INLINE next #-}
148150 next i
149151 | i >= l = Done
150152 | i+3 < l && U32.validate x = Yield (unsafeChr32 x) (i+4)
151− | otherwise = encodingError "UTF-32LE"
153+ | otherwise = decodeError "streamUtf32LE" "UTF-32LE" onErr Nothing (i+1)
152154 where
153155 x = shiftL x4 24 + shiftL x3 16 + shiftL x2 8 + x1
154156 x1 = idx i
@@ -187,6 +189,11 @@ unstream (Stream next s0 len) = unsafePerformIO $ do
187189 memcpy dest' src' (fromIntegral srcLen)
188190 return dest
189191
190−encodingError :: String -> a
191−encodingError encoding =
192− error $ "Data.Text.Encoding.Fusion: Bad " ++ encoding ++ " stream"
192+decodeError :: forall s. String -> String -> OnDecodeError -> Maybe Word8
193+ -> s -> Step s Char
194+decodeError func kind onErr mb i =
195+ case onErr desc mb of
196+ Nothing -> Skip i
197+ Just c -> Yield c i
198+ where desc = "Data.Text.Encoding.Fusion." ++ func ++ ": Invalid " ++
199+ kind ++ " stream"