1{-# LANGUAGE BangPatterns #-}
3-- Module : Data.Text.Encoding
4-- Copyright : (c) 2008, 2009 Tom Harper,
5-- (c) 2009, 2010 Bryan O'Sullivan,
6-- (c) 2009 Duncan Coutts
9-- Maintainer : bos@serpentine.com, rtomharper@googlemail.com,
11-- Stability : experimental
12-- Portability : portable
14-- Functions for converting 'Text' values to and from 'ByteString',
15-- using several standard encodings.
17-- To gain access to a much larger family of encodings, use the
18-- @text-icu@ package: <http://hackage.haskell.org/package/text-icu>
20module Data.Text.Encoding
22 -- * Decoding ByteStrings to Text
31 -- ** Catchable failure
34 -- ** Controllable error handling
41 -- * Encoding Text to ByteStrings
49import Control.Exception (evaluate, try)
50import Data.Bits ((.&.))
51import Data.ByteString as B
52import Data.ByteString.Internal as B
53import Data.ByteString.Unsafe as B
54import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode)
55import Data.Text.Internal (Text(..), textP)
56import Data.Text.UnsafeChar (ord, unsafeWrite)
57import Data.Text.UnsafeShift (shiftL, shiftR)
58import Data.Word (Word8)
59import Foreign.ForeignPtr (withForeignPtr)
60import Foreign.Ptr (plusPtr)
61import Foreign.Storable (poke)
62import System.IO.Unsafe (unsafePerformIO)
63import qualified Data.Text.Array as A
64import qualified Data.Text.Encoding.Fusion as E
65import qualified Data.Text.Encoding.Utf16 as U16
66import qualified Data.Text.Encoding.Utf8 as U8
67import qualified Data.Text.Fusion as F
71-- All of the single-parameter functions for decoding bytestrings
72-- encoded in one of the Unicode Transformation Formats (UTF) operate
73-- in a /strict/ mode: each will throw an exception if given invalid
76-- Each function has a variant, whose name is suffixed with -'With',
77-- that gives greater control over the handling of decoding errors.
78-- For instance, 'decodeUtf8' will throw an exception, but
79-- 'decodeUtf8With' allows the programmer to determine what to do on a
82-- | Decode a 'ByteString' containing 7-bit ASCII encoded text.
83decodeASCII :: ByteString -> Text
84decodeASCII bs = F.unstream (E.streamASCII bs)
85{-# INLINE decodeASCII #-}
87-- | Decode a 'ByteString' containing UTF-8 encoded text.
88decodeUtf8With :: OnDecodeError -> ByteString -> Text
89decodeUtf8With onErr bs = textP (fst a) 0 (snd a)
91 a = A.run2 (A.new len >>= outer 0 0)
93 outer n0 m0 arr = go n0 m0
100 idx = B.unsafeIndex bs
102 _| m >= len -> return (arr,n)
103 | U8.validate1 x1 -> do
104 A.unsafeWrite arr n (fromIntegral x1)
106 | m+1 < len && U8.validate2 x1 x2 -> do
107 w <- unsafeWrite arr n (U8.chr2 x1 x2)
109 | m+2 < len && U8.validate3 x1 x2 x3 -> do
110 w <- unsafeWrite arr n (U8.chr3 x1 x2 x3)
112 | m+3 < len && U8.validate4 x1 x2 x3 x4 -> do
113 w <- unsafeWrite arr n (U8.chr4 x1 x2 x3 x4)
115 | otherwise -> case onErr desc (Just x1) of
116 Nothing -> go n (m+1)
118 w <- unsafeWrite arr n c
120 desc = "Data.Text.Encoding.decodeUtf8: Invalid UTF-8 stream"
121{-# INLINE[0] decodeUtf8With #-}
123-- | Decode a 'ByteString' containing UTF-8 encoded text that is known
126-- If the input contains any invalid UTF-8 data, an exception will be
127-- thrown that cannot be caught in pure code. For more control over
128-- the handling of invalid data, use 'decodeUtf8'' or
130decodeUtf8 :: ByteString -> Text
131decodeUtf8 = decodeUtf8With strictDecode
132{-# INLINE[0] decodeUtf8 #-}
133{-# RULES "STREAM stream/decodeUtf8 fusion" [1]
134 forall bs. F.stream (decodeUtf8 bs) = E.streamUtf8 strictDecode bs #-}
136-- | Decode a 'ByteString' containing UTF-8 encoded text..
138-- If the input contains any invalid UTF-8 data, the relevant
139-- exception will be returned, otherwise the decoded text.
140decodeUtf8' :: ByteString -> Either UnicodeException Text
141decodeUtf8' = unsafePerformIO . try . evaluate . decodeUtf8With strictDecode
142{-# INLINE decodeUtf8' #-}
144-- | Encode text using UTF-8 encoding.
145encodeUtf8 :: Text -> ByteString
146encodeUtf8 (Text arr off len) = unsafePerformIO $ do
147 let size0 = min len 4
148 mallocByteString size0 >>= start size0 off 0
150 start size n0 m0 fp = withForeignPtr fp $ loop n0 m0
152 loop n1 m1 ptr = go n1 m1
155 | n-off == len = return $! PS fp 0 m
156 | size-m < 4 = {-# SCC "encodeUtf8/resize" #-} do
157 let newSize = size `shiftL` 1
158 fp' <- mallocByteString newSize
159 withForeignPtr fp' $ \ptr' -> memcpy ptr' ptr (fromIntegral m)
160 start newSize n m fp'
162 let poke8 k v = poke (ptr `plusPtr` k) (fromIntegral v :: Word8)
163 w = A.unsafeIndex arr n
169 poke8 m $ (w `shiftR` 6) + 0xC0
170 poke8 (m+1) $ (w .&. 0x3f) + 0x80
172 | 0xD800 <= w && w <= 0xDBFF -> do
173 let c = ord $ U16.chr2 w (A.unsafeIndex arr (n+1))
174 poke8 m $ (c `shiftR` 18) + 0xF0
175 poke8 (m+1) $ ((c `shiftR` 12) .&. 0x3F) + 0x80
176 poke8 (m+2) $ ((c `shiftR` 6) .&. 0x3F) + 0x80
177 poke8 (m+3) $ (c .&. 0x3F) + 0x80
180 poke8 m $ (w `shiftR` 12) + 0xE0
181 poke8 (m+1) $ ((w `shiftR` 6) .&. 0x3F) + 0x80
182 poke8 (m+2) $ (w .&. 0x3F) + 0x80
184{- INLINE encodeUtf8 #-}
186-- | Decode text from little endian UTF-16 encoding.
187decodeUtf16LEWith :: OnDecodeError -> ByteString -> Text
188decodeUtf16LEWith onErr bs = F.unstream (E.streamUtf16LE onErr bs)
189{-# INLINE decodeUtf16LEWith #-}
191-- | Decode text from little endian UTF-16 encoding.
193-- If the input contains any invalid little endian UTF-16 data, an
194-- exception will be thrown. For more control over the handling of
195-- invalid data, use 'decodeUtf16LEWith'.
196decodeUtf16LE :: ByteString -> Text
197decodeUtf16LE = decodeUtf16LEWith strictDecode
198{-# INLINE decodeUtf16LE #-}
200-- | Decode text from big endian UTF-16 encoding.
201decodeUtf16BEWith :: OnDecodeError -> ByteString -> Text
202decodeUtf16BEWith onErr bs = F.unstream (E.streamUtf16BE onErr bs)
203{-# INLINE decodeUtf16BEWith #-}
205-- | Decode text from big endian UTF-16 encoding.
207-- If the input contains any invalid big endian UTF-16 data, an
208-- exception will be thrown. For more control over the handling of
209-- invalid data, use 'decodeUtf16BEWith'.
210decodeUtf16BE :: ByteString -> Text
211decodeUtf16BE = decodeUtf16BEWith strictDecode
212{-# INLINE decodeUtf16BE #-}
214-- | Encode text using little endian UTF-16 encoding.
215encodeUtf16LE :: Text -> ByteString
216encodeUtf16LE txt = E.unstream (E.restreamUtf16LE (F.stream txt))
217{-# INLINE encodeUtf16LE #-}
219-- | Encode text using big endian UTF-16 encoding.
220encodeUtf16BE :: Text -> ByteString
221encodeUtf16BE txt = E.unstream (E.restreamUtf16BE (F.stream txt))
222{-# INLINE encodeUtf16BE #-}
224-- | Decode text from little endian UTF-32 encoding.
225decodeUtf32LEWith :: OnDecodeError -> ByteString -> Text
226decodeUtf32LEWith onErr bs = F.unstream (E.streamUtf32LE onErr bs)
227{-# INLINE decodeUtf32LEWith #-}
229-- | Decode text from little endian UTF-32 encoding.
231-- If the input contains any invalid little endian UTF-32 data, an
232-- exception will be thrown. For more control over the handling of
233-- invalid data, use 'decodeUtf32LEWith'.
234decodeUtf32LE :: ByteString -> Text
235decodeUtf32LE = decodeUtf32LEWith strictDecode
236{-# INLINE decodeUtf32LE #-}
238-- | Decode text from big endian UTF-32 encoding.
239decodeUtf32BEWith :: OnDecodeError -> ByteString -> Text
240decodeUtf32BEWith onErr bs = F.unstream (E.streamUtf32BE onErr bs)
241{-# INLINE decodeUtf32BEWith #-}
243-- | Decode text from big endian UTF-32 encoding.
245-- If the input contains any invalid big endian UTF-32 data, an
246-- exception will be thrown. For more control over the handling of
247-- invalid data, use 'decodeUtf32BEWith'.
248decodeUtf32BE :: ByteString -> Text
249decodeUtf32BE = decodeUtf32BEWith strictDecode
250{-# INLINE decodeUtf32BE #-}
252-- | Encode text using little endian UTF-32 encoding.
253encodeUtf32LE :: Text -> ByteString
254encodeUtf32LE txt = E.unstream (E.restreamUtf32LE (F.stream txt))
255{-# INLINE encodeUtf32LE #-}
257-- | Encode text using big endian UTF-32 encoding.
258encodeUtf32BE :: Text -> ByteString
259encodeUtf32BE txt = E.unstream (E.restreamUtf32BE (F.stream txt))
260{-# INLINE encodeUtf32BE #-}
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list