@@ -43,6 +43,10 @@ module Data.Text.Encoding
43 , decodeUtf32LEWith
43 , decodeUtf32LEWith
44 , decodeUtf32BEWith
44 , decodeUtf32BEWith
46+ -- ** Streaming decoding with controllable error handling
46 -- * Encoding Text to ByteStrings
50 -- * Encoding Text to ByteStrings
47 , encodeUtf8
51 , encodeUtf8
48 , encodeUtf16LE
52 , encodeUtf16LE
@@ -57,6 +61,7 @@ import Control.Monad.ST.Unsafe (unsafeIOToST, unsafeSTToIO)
58import Control.Monad.ST (unsafeIOToST, unsafeSTToIO)
62import Control.Monad.ST (unsafeIOToST, unsafeSTToIO)
64+import Control.Monad.ST (runST)
60import Data.Bits ((.&.))
65import Data.Bits ((.&.))
61import Data.ByteString as B
66import Data.ByteString as B
62import Data.ByteString.Internal as B
67import Data.ByteString.Internal as B
@@ -65,7 +70,7 @@ import Data.Text.Internal (Text(..), safe, textP)
65import Data.Text.Private (runText)
70import Data.Text.Private (runText)
66import Data.Text.UnsafeChar (ord, unsafeWrite)
71import Data.Text.UnsafeChar (ord, unsafeWrite)
67import Data.Text.UnsafeShift (shiftL, shiftR)
72import Data.Text.UnsafeShift (shiftL, shiftR)
68−import Data.Word (Word8)
73+import Data.Word (Word8, Word32)
69import Foreign.C.Types (CSize)
74import Foreign.C.Types (CSize)
70import Foreign.ForeignPtr (withForeignPtr)
75import Foreign.ForeignPtr (withForeignPtr)
71import Foreign.Marshal.Utils (with)
76import Foreign.Marshal.Utils (with)
@@ -139,6 +144,56 @@ decodeUtf8With onErr (PS fp off len) = runText $ \done -> do
139 desc = "Data.Text.Encoding.decodeUtf8: Invalid UTF-8 stream"
144 desc = "Data.Text.Encoding.decodeUtf8: Invalid UTF-8 stream"
140{- INLINE[0] decodeUtf8With #-}
145{- INLINE[0] decodeUtf8With #-}
147+data Decoder = Some !Text (ByteString -> Decoder)
149+-- | (codepoint, state)
150+type DecoderState = (Word32, Word32)
152+decodeUtf8With' :: OnDecodeError -> ByteString -> Decoder
153+decodeUtf8With' onErr = decodeChunk (0,0)
155+ -- We create a slightly larger than necessary buffer to accomodate a
156+ -- potential surrogate pair started in the last buffer
157+ decodeChunk :: DecoderState -> ByteString -> Decoder
158+ decodeChunk (codepoint0,state0) (PS fp off len) =
159+ runST $ (unsafeIOToST . decodeChunkToBuffer) =<< A.new (len+1)
161+ decodeChunkToBuffer :: A.MArray s -> IO Decoder
162+ decodeChunkToBuffer dest = withForeignPtr fp $ \ptr ->
163+ with (0::CSize) $ \destOffPtr ->
164+ with codepoint0 $ \codepointPtr ->
165+ with state0 $ \statePtr ->
166+ let end = ptr `plusPtr` (off + len)
168+ curPtr' <- c_decode_utf8_with_state (A.maBA dest) destOffPtr curPtr end
169+ codepointPtr statePtr
170+ state <- peek statePtr
173+ -- We encountered an encoding error
175+ case onErr desc (Just x) of
176+ Nothing -> loop $ curPtr' `plusPtr` 1
178+ destOff <- peek destOffPtr
180+ unsafeWrite dest (fromIntegral destOff) (safe c)
181+ poke destOffPtr (destOff + fromIntegral w)
183+ loop $ curPtr' `plusPtr` 1
186+ -- We encountered the end of the buffer while decoding
187+ n <- peek destOffPtr
188+ codepoint <- peek codepointPtr
189+ chunkText <- unsafeSTToIO $ do
190+ arr <- A.unsafeFreeze dest
191+ return $! textP arr 0 (fromIntegral n)
192+ return $ Some chunkText $ decodeChunk (codepoint, state)
193+ in loop (ptr `plusPtr` off)
194+ desc = "Data.Text.Encoding.decodeUtf8With': Invalid UTF-8 stream"
195+{- INLINE[0] decodeUtf8With' #-}
142-- | Decode a 'ByteString' containing UTF-8 encoded text that is known
197-- | Decode a 'ByteString' containing UTF-8 encoded text that is known
143-- to be valid.
198-- to be valid.
@@ -296,5 +351,10 @@ foreign import ccall unsafe "_hs_text_decode_utf8" c_decode_utf8
296 :: MutableByteArray# s -> Ptr CSize
351 :: MutableByteArray# s -> Ptr CSize
297 -> Ptr Word8 -> Ptr Word8 -> IO (Ptr Word8)
352 -> Ptr Word8 -> Ptr Word8 -> IO (Ptr Word8)
354+foreign import ccall unsafe "_hs_text_decode_utf8_state" c_decode_utf8_with_state
355+ :: MutableByteArray# s -> Ptr CSize
356+ -> Ptr Word8 -> Ptr Word8
357+ -> Ptr Word32 -> Ptr Word32 -> IO (Ptr Word8)
299foreign import ccall unsafe "_hs_text_decode_latin1" c_decode_latin1
359foreign import ccall unsafe "_hs_text_decode_latin1" c_decode_latin1
300 :: MutableByteArray# s -> Ptr Word8 -> Ptr Word8 -> IO ()
360 :: MutableByteArray# s -> Ptr Word8 -> Ptr Word8 -> IO ()