@@ -1,4 +1,4 @@
1−{-# LANGUAGE BangPatterns #-}
1+{-# LANGUAGE BangPatterns, Rank2Types #-}
4-- Module : Data.Text.Encoding.Fusion
4-- Module : Data.Text.Encoding.Fusion
@@ -35,6 +35,7 @@ import Control.Exception (assert)
35import Data.ByteString as B
35import Data.ByteString as B
36import Data.ByteString.Internal (ByteString(..), mallocByteString, memcpy)
36import Data.ByteString.Internal (ByteString(..), mallocByteString, memcpy)
37import Data.Text.Fusion (Step(..), Stream(..))
37import Data.Text.Fusion (Step(..), Stream(..))
38+import Data.Text.Encoding.Error
38import Data.Text.Encoding.Fusion.Common
39import Data.Text.Encoding.Fusion.Common
39import Data.Text.UnsafeChar (unsafeChr, unsafeChr8, unsafeChr32)
40import Data.Text.UnsafeChar (unsafeChr, unsafeChr8, unsafeChr32)
40import Data.Text.UnsafeShift (shiftL)
41import Data.Text.UnsafeShift (shiftL)
@@ -62,8 +63,8 @@ streamASCII bs = Stream next 0 l
63-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using UTF-8
64-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using UTF-8
64-- encoding.
65-- encoding.
65−streamUtf8 :: ByteString -> Stream Char
66+streamUtf8 :: OnDecodeError -> ByteString -> Stream Char
66−streamUtf8 bs = Stream next 0 l
67+streamUtf8 onErr bs = Stream next 0 l
68 l = B.length bs
69 l = B.length bs
69 {-# INLINE next #-}
70 {-# INLINE next #-}
@@ -73,8 +74,9 @@ streamUtf8 bs = Stream next 0 l
73 | i+1 < l && U8.validate2 x1 x2 = Yield (U8.chr2 x1 x2) (i+2)
74 | i+1 < l && U8.validate2 x1 x2 = Yield (U8.chr2 x1 x2) (i+2)
74 | i+2 < l && U8.validate3 x1 x2 x3 = Yield (U8.chr3 x1 x2 x3) (i+3)
75 | i+2 < l && U8.validate3 x1 x2 x3 = Yield (U8.chr3 x1 x2 x3) (i+3)
75 | i+3 < l && U8.validate4 x1 x2 x3 x4 = Yield (U8.chr4 x1 x2 x3 x4) (i+4)
76 | 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)
79+ mx = if i >= l then Nothing else Just x1
78 x1 = idx i
80 x1 = idx i
79 x2 = idx (i + 1)
81 x2 = idx (i + 1)
80 x3 = idx (i + 2)
82 x3 = idx (i + 2)
@@ -84,8 +86,8 @@ streamUtf8 bs = Stream next 0 l
85-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using little
87-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using little
86-- endian UTF-16 encoding.
88-- endian UTF-16 encoding.
87−streamUtf16LE :: ByteString -> Stream Char
89+streamUtf16LE :: OnDecodeError -> ByteString -> Stream Char
88−streamUtf16LE bs = Stream next 0 l
90+streamUtf16LE onErr bs = Stream next 0 l
90 l = B.length bs
92 l = B.length bs
91 {-# INLINE next #-}
93 {-# INLINE next #-}
@@ -93,7 +95,7 @@ streamUtf16LE bs = Stream next 0 l
93 | i >= l = Done
95 | i >= l = Done
94 | i+1 < l && U16.validate1 x1 = Yield (unsafeChr x1) (i+2)
96 | i+1 < l && U16.validate1 x1 = Yield (unsafeChr x1) (i+2)
95 | i+3 < l && U16.validate2 x1 x2 = Yield (U16.chr2 x1 x2) (i+4)
97 | 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)
98 x1 = idx i + (idx (i + 1) `shiftL` 8)
100 x1 = idx i + (idx (i + 1) `shiftL` 8)
99 x2 = idx (i + 2) + (idx (i + 3) `shiftL` 8)
101 x2 = idx (i + 2) + (idx (i + 3) `shiftL` 8)
@@ -102,8 +104,8 @@ streamUtf16LE bs = Stream next 0 l
103-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using big
105-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using big
104-- endian UTF-16 encoding.
106-- endian UTF-16 encoding.
105−streamUtf16BE :: ByteString -> Stream Char
107+streamUtf16BE :: OnDecodeError -> ByteString -> Stream Char
106−streamUtf16BE bs = Stream next 0 l
108+streamUtf16BE onErr bs = Stream next 0 l
108 l = B.length bs
110 l = B.length bs
109 {-# INLINE next #-}
111 {-# INLINE next #-}
@@ -111,7 +113,7 @@ streamUtf16BE bs = Stream next 0 l
111 | i >= l = Done
113 | i >= l = Done
112 | i+1 < l && U16.validate1 x1 = Yield (unsafeChr x1) (i+2)
114 | i+1 < l && U16.validate1 x1 = Yield (unsafeChr x1) (i+2)
113 | i+3 < l && U16.validate2 x1 x2 = Yield (U16.chr2 x1 x2) (i+4)
115 | 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)
116 x1 = (idx i `shiftL` 8) + idx (i + 1)
118 x1 = (idx i `shiftL` 8) + idx (i + 1)
117 x2 = (idx (i + 2) `shiftL` 8) + idx (i + 3)
119 x2 = (idx (i + 2) `shiftL` 8) + idx (i + 3)
@@ -120,15 +122,15 @@ streamUtf16BE bs = Stream next 0 l
121-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using big
123-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using big
122-- endian UTF-32 encoding.
124-- endian UTF-32 encoding.
123−streamUtf32BE :: ByteString -> Stream Char
125+streamUtf32BE :: OnDecodeError -> ByteString -> Stream Char
124−streamUtf32BE bs = Stream next 0 l
126+streamUtf32BE onErr bs = Stream next 0 l
126 l = B.length bs
128 l = B.length bs
127 {-# INLINE next #-}
129 {-# INLINE next #-}
129 | i >= l = Done
131 | i >= l = Done
130 | i+3 < l && U32.validate x = Yield (unsafeChr32 x) (i+4)
132 | 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)
133 x = shiftL x1 24 + shiftL x2 16 + shiftL x3 8 + x4
135 x = shiftL x1 24 + shiftL x2 16 + shiftL x3 8 + x4
134 x1 = idx i
136 x1 = idx i
@@ -140,15 +142,15 @@ streamUtf32BE bs = Stream next 0 l
141-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using little
143-- | /O(n)/ Convert a 'ByteString' into a 'Stream Char', using little
142-- endian UTF-32 encoding.
144-- endian UTF-32 encoding.
143−streamUtf32LE :: ByteString -> Stream Char
145+streamUtf32LE :: OnDecodeError -> ByteString -> Stream Char
144−streamUtf32LE bs = Stream next 0 l
146+streamUtf32LE onErr bs = Stream next 0 l
146 l = B.length bs
148 l = B.length bs
147 {-# INLINE next #-}
149 {-# INLINE next #-}
149 | i >= l = Done
151 | i >= l = Done
150 | i+3 < l && U32.validate x = Yield (unsafeChr32 x) (i+4)
152 | 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)
153 x = shiftL x4 24 + shiftL x3 16 + shiftL x2 8 + x1
155 x = shiftL x4 24 + shiftL x3 16 + shiftL x2 8 + x1
154 x1 = idx i
156 x1 = idx i
@@ -187,6 +189,11 @@ unstream (Stream next s0 len) = unsafePerformIO $ do
187 memcpy dest' src' (fromIntegral srcLen)
189 memcpy dest' src' (fromIntegral srcLen)
188 return dest
190 return dest
190−encodingError :: String -> a
192+decodeError :: forall s. String -> String -> OnDecodeError -> Maybe Word8
191−encodingError encoding =
193+ -> s -> Step s Char
194+decodeError func kind onErr mb i =
195+ case onErr desc mb of
192− error $ "Data.Text.Encoding.Fusion: Bad " ++ encoding ++ " stream"
198+ where desc = "Data.Text.Encoding.Fusion." ++ func ++ ": Invalid " ++