on text-jsstring help sign in
text/Data/Text/Encoding.hs
507 lines 20 kB history raw
1{-# LANGUAGE BangPatterns, CPP, ForeignFunctionInterface, GeneralizedNewtypeDeriving, MagicHash,
2 UnliftedFFITypes #-}
3#if __GLASGOW_HASKELL__ >= 702
4{-# LANGUAGE Trustworthy #-}
5#endif
6{-# LANGUAGE UnboxedTuples #-}
7-- |
8-- Module : Data.Text.Encoding
9-- Copyright : (c) 2009, 2010, 2011 Bryan O'Sullivan,
10-- (c) 2009 Duncan Coutts,
11-- (c) 2008, 2009 Tom Harper
12--
13-- License : BSD-style
14-- Maintainer : bos@serpentine.com
15-- Stability : experimental
16-- Portability : portable
17--
18-- Functions for converting 'Text' values to and from 'ByteString',
19-- using several standard encodings.
20--
21-- To gain access to a much larger family of encodings, use the
22-- @text-icu@ package: <http://hackage.haskell.org/package/text-icu>
24module Data.Text.Encoding
25 (
26 -- * Decoding ByteStrings to Text
27 -- $strict
28 decodeASCII
29 , decodeLatin1
30 , decodeUtf8
31 , decodeUtf16LE
32 , decodeUtf16BE
33 , decodeUtf32LE
34 , decodeUtf32BE
36 -- ** Catchable failure
37 , decodeUtf8'
39 -- ** Controllable error handling
40 , decodeUtf8With
41 , decodeUtf16LEWith
42 , decodeUtf16BEWith
43 , decodeUtf32LEWith
44 , decodeUtf32BEWith
46 -- ** Stream oriented decoding
47 -- $stream
48 , streamDecodeUtf8
49 , streamDecodeUtf8With
50 , Decoding(..)
52 -- * Encoding Text to ByteStrings
53 , encodeUtf8
54 , encodeUtf16LE
55 , encodeUtf16BE
56 , encodeUtf32LE
57 , encodeUtf32BE
59 -- * Encoding Text using ByteString Builders
60 , encodeUtf8Builder
61 , encodeUtf8BuilderEscaped
62 ) where
64#if __GLASGOW_HASKELL__ >= 702
65import Control.Monad.ST.Unsafe (unsafeIOToST, unsafeSTToIO)
66#else
67import Control.Monad.ST (unsafeIOToST, unsafeSTToIO)
68#endif
70import Control.Exception (evaluate, try)
71import Control.Monad.ST (runST)
72import Data.Bits ((.&.))
73import Data.ByteString as B
74import Data.ByteString.Internal as B hiding (c2w)
75import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode)
76import Data.Text.Internal (Text(..), safe, text)
77import Data.Text.Internal.Private (runText)
78import Data.Text.Internal.Unsafe.Char (ord, unsafeWrite)
79import Data.Text.Internal.Unsafe.Shift (shiftR)
80#ifndef __GHCJS__
81import Data.Text.Show ()
82#endif
83import Data.Text.Unsafe (unsafeDupablePerformIO)
84import Data.Word (Word8, Word32)
85import Foreign.C.Types (CSize(..))
86import Foreign.ForeignPtr (withForeignPtr)
87import Foreign.Marshal.Utils (with)
88import Foreign.Ptr (Ptr, minusPtr, nullPtr, plusPtr)
89import Foreign.Storable (Storable, peek, poke)
90import GHC.Base (ByteArray#, MutableByteArray#)
91import qualified Data.ByteString.Builder as B
92import qualified Data.ByteString.Builder.Internal as B hiding (empty, append)
93import qualified Data.ByteString.Builder.Prim as BP
94import qualified Data.ByteString.Builder.Prim.Internal as BP
95import qualified Data.Text.Array as A
96import qualified Data.Text.Internal.Encoding.Fusion as E
97import qualified Data.Text.Internal.Encoding.Utf16 as U16
98import qualified Data.Text.Internal.Fusion as F
100#include "text_cbits.h"
101#ifdef __GHCJS__
102import GHC.Exts (Int(..), Int#)
103import Data.ByteString (ByteString)
104import GHCJS.Buffer
105import Data.JSString (JSString)
106#endif
108-- $strict
109--
110-- All of the single-parameter functions for decoding bytestrings
111-- encoded in one of the Unicode Transformation Formats (UTF) operate
112-- in a /strict/ mode: each will throw an exception if given invalid
113-- input.
114--
115-- Each function has a variant, whose name is suffixed with -'With',
116-- that gives greater control over the handling of decoding errors.
117-- For instance, 'decodeUtf8' will throw an exception, but
118-- 'decodeUtf8With' allows the programmer to determine what to do on a
119-- decoding error.
121-- | /Deprecated/. Decode a 'ByteString' containing 7-bit ASCII
122-- encoded text.
123decodeASCII :: ByteString -> Text
124decodeASCII = decodeUtf8
125{-# DEPRECATED decodeASCII "Use decodeUtf8 instead" #-}
127-- | Decode a 'ByteString' containing Latin-1 (aka ISO-8859-1) encoded text.
128--
129-- 'decodeLatin1' is semantically equivalent to
130-- @Data.Text.pack . Data.ByteString.Char8.unpack@
131decodeLatin1 :: ByteString -> Text
132decodeLatin1 (PS fp off len) = text a 0 len
133 where
134 a = A.run (A.new len >>= unsafeIOToST . go)
135 go dest = withForeignPtr fp $ \ptr -> do
136 c_decode_latin1 (A.maBA dest) (ptr `plusPtr` off) (ptr `plusPtr` (off+len))
137 return dest
139-- | Decode a 'ByteString' containing UTF-8 encoded text.
140decodeUtf8With :: OnDecodeError -> ByteString -> Text
141decodeUtf8With onErr (PS fp off len) = runText $ \done -> do
142 let go dest = withForeignPtr fp $ \ptr ->
143 with (0::CSize) $ \destOffPtr -> do
144 let end = ptr `plusPtr` (off + len)
145 loop curPtr = do
146 curPtr' <- c_decode_utf8 (A.maBA dest) destOffPtr curPtr end
147 if curPtr' == end
148 then do
149 n <- peek destOffPtr
150 unsafeSTToIO (done dest (fromIntegral n))
151 else do
152 x <- peek curPtr'
153 case onErr desc (Just x) of
154 Nothing -> loop $ curPtr' `plusPtr` 1
155 Just c -> do
156 destOff <- peek destOffPtr
157 w <- unsafeSTToIO $
158 unsafeWrite dest (fromIntegral destOff) (safe c)
159 poke destOffPtr (destOff + fromIntegral w)
160 loop $ curPtr' `plusPtr` 1
161 loop (ptr `plusPtr` off)
162 (unsafeIOToST . go) =<< A.new len
163 where
164 desc = "Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream"
165{- INLINE[0] decodeUtf8With #-}
167-- $stream
168--
169-- The 'streamDecodeUtf8' and 'streamDecodeUtf8With' functions accept
170-- a 'ByteString' that represents a possibly incomplete input (e.g. a
171-- packet from a network stream) that may not end on a UTF-8 boundary.
172--
173-- 1. The maximal prefix of 'Text' that could be decoded from the
174-- given input.
175--
176-- 2. The suffix of the 'ByteString' that could not be decoded due to
177-- insufficient input.
178--
179-- 3. A function that accepts another 'ByteString'. That string will
180-- be assumed to directly follow the string that was passed as
181-- input to the original function, and it will in turn be decoded.
182--
183-- To help understand the use of these functions, consider the Unicode
184-- string @\"hi &#9731;\"@. If encoded as UTF-8, this becomes @\"hi
185-- \\xe2\\x98\\x83\"@; the final @\'&#9731;\'@ is encoded as 3 bytes.
186--
187-- Now suppose that we receive this encoded string as 3 packets that
188-- are split up on untidy boundaries: @[\"hi \\xe2\", \"\\x98\",
189-- \"\\x83\"]@. We cannot decode the entire Unicode string until we
190-- have received all three packets, but we would like to make progress
191-- as we receive each one.
192--
193-- @
194-- ghci> let s0\@('Some' _ _ f0) = 'streamDecodeUtf8' \"hi \\xe2\"
195-- ghci> s0
196-- 'Some' \"hi \" \"\\xe2\" _
197-- @
198--
199-- We use the continuation @f0@ to decode our second packet.
200--
201-- @
202-- ghci> let s1\@('Some' _ _ f1) = f0 \"\\x98\"
203-- ghci> s1
204-- 'Some' \"\" \"\\xe2\\x98\"
205-- @
206--
207-- We could not give @f0@ enough input to decode anything, so it
208-- returned an empty string. Once we feed our second continuation @f1@
209-- the last byte of input, it will make progress.
210--
211-- @
212-- ghci> let s2\@('Some' _ _ f2) = f1 \"\\x83\"
213-- ghci> s2
214-- 'Some' \"\\x2603\" \"\" _
215-- @
216--
217-- If given invalid input, an exception will be thrown by the function
218-- or continuation where it is encountered.
220-- | A stream oriented decoding result.
221data Decoding = Some Text ByteString (ByteString -> Decoding)
223instance Show Decoding where
224 showsPrec d (Some t bs _) = showParen (d > prec) $
225 showString "Some " . showsPrec prec' t .
226 showChar ' ' . showsPrec prec' bs .
227 showString " _"
228 where prec = 10; prec' = prec + 1
230newtype CodePoint = CodePoint Word32 deriving (Eq, Show, Num, Storable)
231newtype DecoderState = DecoderState Word32 deriving (Eq, Show, Num, Storable)
233-- | Decode, in a stream oriented way, a 'ByteString' containing UTF-8
234-- encoded text that is known to be valid.
235--
236-- If the input contains any invalid UTF-8 data, an exception will be
237-- thrown (either by this function or a continuation) that cannot be
238-- caught in pure code. For more control over the handling of invalid
239-- data, use 'streamDecodeUtf8With'.
240streamDecodeUtf8 :: ByteString -> Decoding
241streamDecodeUtf8 = streamDecodeUtf8With strictDecode
243-- | Decode, in a stream oriented way, a 'ByteString' containing UTF-8
244-- encoded text.
245streamDecodeUtf8With :: OnDecodeError -> ByteString -> Decoding
246streamDecodeUtf8With onErr = decodeChunk B.empty 0 0
247 where
248 -- We create a slightly larger than necessary buffer to accommodate a
249 -- potential surrogate pair started in the last buffer
250 decodeChunk :: ByteString -> CodePoint -> DecoderState -> ByteString
251 -> Decoding
252 decodeChunk undecoded0 codepoint0 state0 bs@(PS fp off len) =
253 runST $ (unsafeIOToST . decodeChunkToBuffer) =<< A.new (len+1)
254 where
255 decodeChunkToBuffer :: A.MArray s -> IO Decoding
256 decodeChunkToBuffer dest = withForeignPtr fp $ \ptr ->
257 with (0::CSize) $ \destOffPtr ->
258 with codepoint0 $ \codepointPtr ->
259 with state0 $ \statePtr ->
260 with nullPtr $ \curPtrPtr ->
261 let end = ptr `plusPtr` (off + len)
262 loop curPtr = do
263 poke curPtrPtr curPtr
264 curPtr' <- c_decode_utf8_with_state (A.maBA dest) destOffPtr
265 curPtrPtr end codepointPtr statePtr
266 state <- peek statePtr
267 case state of
268 UTF8_REJECT -> do
269 -- We encountered an encoding error
270 x <- peek curPtr'
271 poke statePtr 0
272 case onErr desc (Just x) of
273 Nothing -> loop $ curPtr' `plusPtr` 1
274 Just c -> do
275 destOff <- peek destOffPtr
276 w <- unsafeSTToIO $
277 unsafeWrite dest (fromIntegral destOff) (safe c)
278 poke destOffPtr (destOff + fromIntegral w)
279 loop $ curPtr' `plusPtr` 1
281 _ -> do
282 -- We encountered the end of the buffer while decoding
283 n <- peek destOffPtr
284 codepoint <- peek codepointPtr
285 chunkText <- unsafeSTToIO $ do
286 arr <- A.unsafeFreeze dest
287 return $! text arr 0 (fromIntegral n)
288 lastPtr <- peek curPtrPtr
289 let left = lastPtr `minusPtr` curPtr
290 !undecoded = case state of
291 UTF8_ACCEPT -> B.empty
292 _ -> B.append undecoded0 (B.drop left bs)
293 return $ Some chunkText undecoded
294 (decodeChunk undecoded codepoint state)
295 in loop (ptr `plusPtr` off)
296 desc = "Data.Text.Internal.Encoding.streamDecodeUtf8With: Invalid UTF-8 stream"
298-- | Decode a 'ByteString' containing UTF-8 encoded text that is known
299-- to be valid.
300--
301-- If the input contains any invalid UTF-8 data, an exception will be
302-- thrown that cannot be caught in pure code. For more control over
303-- the handling of invalid data, use 'decodeUtf8'' or
304-- 'decodeUtf8With'.
305decodeUtf8 :: ByteString -> Text
306decodeUtf8 = decodeUtf8With strictDecode
307{-# INLINE[0] decodeUtf8 #-}
308{-# RULES "STREAM stream/decodeUtf8 fusion" [1]
309 forall bs. F.stream (decodeUtf8 bs) = E.streamUtf8 strictDecode bs #-}
311-- | Decode a 'ByteString' containing UTF-8 encoded text.
312--
313-- If the input contains any invalid UTF-8 data, the relevant
314-- exception will be returned, otherwise the decoded text.
315decodeUtf8' :: ByteString -> Either UnicodeException Text
316decodeUtf8' = unsafeDupablePerformIO . try . evaluate . decodeUtf8With strictDecode
317{-# INLINE decodeUtf8' #-}
319-- | Encode text to a ByteString 'B.Builder' using UTF-8 encoding.
320encodeUtf8Builder :: Text -> B.Builder
321encodeUtf8Builder = encodeUtf8BuilderEscaped (BP.liftFixedToBounded BP.word8)
323-- | Encode text using UTF-8 encoding and escape the ASCII characters using
324-- a 'BP.BoundedPrim'.
325--
326-- Use this function is to implement efficient encoders for text-based formats
327-- like JSON or HTML.
328{-# INLINE encodeUtf8BuilderEscaped #-}
329-- TODO: Extend documentation with references to source code in @blaze-html@
330-- or @aeson@ that uses this function.
331encodeUtf8BuilderEscaped :: BP.BoundedPrim Word8 -> Text -> B.Builder
332encodeUtf8BuilderEscaped be =
333 -- manual eta-expansion to ensure inlining works as expected
334 \txt -> B.builder (mkBuildstep txt)
335 where
336 bound = max 4 $ BP.sizeBound be
338#ifndef __GHCJS__
339 mkBuildstep (Text arr off len) !k =
340#else
341 mkBuildstep (Text t) !k =
342#endif
343 outerLoop off
344 where
345#ifdef __GHCJS__
346 off = 0
347 (arr, len) = let (# ba, len' #) = js_fromString t
348 in (A.Array ba, I# len')
349#endif
350 iend = off + len
352 outerLoop !i0 !br@(B.BufferRange op0 ope)
353 | i0 >= iend = k br
354 | outRemaining > 0 = goPartial (i0 + min outRemaining inpRemaining)
355 -- TODO: Use a loop with an integrated bound's check if outRemaining
356 -- is smaller than 8, as this will save on divisions.
357 | otherwise = return $ B.bufferFull bound op0 (outerLoop i0)
358 where
359 outRemaining = (ope `minusPtr` op0) `div` bound
360 inpRemaining = iend - i0
362 goPartial !iendTmp = go i0 op0
363 where
364 go !i !op
365 | i < iendTmp = case A.unsafeIndex arr i of
366 w | w <= 0x7F -> do
367 BP.runB be (fromIntegral w) op >>= go (i + 1)
368 | w <= 0x7FF -> do
369 poke8 0 $ (w `shiftR` 6) + 0xC0
370 poke8 1 $ (w .&. 0x3f) + 0x80
371 go (i + 1) (op `plusPtr` 2)
372 | 0xD800 <= w && w <= 0xDBFF -> do
373 let c = ord $ U16.chr2 w (A.unsafeIndex arr (i+1))
374 poke8 0 $ (c `shiftR` 18) + 0xF0
375 poke8 1 $ ((c `shiftR` 12) .&. 0x3F) + 0x80
376 poke8 2 $ ((c `shiftR` 6) .&. 0x3F) + 0x80
377 poke8 3 $ (c .&. 0x3F) + 0x80
378 go (i + 2) (op `plusPtr` 4)
379 | otherwise -> do
380 poke8 0 $ (w `shiftR` 12) + 0xE0
381 poke8 1 $ ((w `shiftR` 6) .&. 0x3F) + 0x80
382 poke8 2 $ (w .&. 0x3F) + 0x80
383 go (i + 1) (op `plusPtr` 3)
384 | otherwise =
385 outerLoop i (B.BufferRange op ope)
386 where
387 poke8 j v = poke (op `plusPtr` j) (fromIntegral v :: Word8)
389-- | Encode text using UTF-8 encoding.
390encodeUtf8 :: Text -> ByteString
391#ifndef __GHCJS__
392encodeUtf8 (Text arr off len)
393 | len == 0 = B.empty
394 | otherwise = unsafeDupablePerformIO $ do
395 fp <- mallocByteString (len*4)
396 withForeignPtr fp $ \ptr ->
397 with ptr $ \destPtr -> do
398 c_encode_utf8 destPtr (A.aBA arr) (fromIntegral off) (fromIntegral len)
399 newDest <- peek destPtr
400 let utf8len = newDest `minusPtr` ptr
401 if utf8len >= len `shiftR` 1
402 then return (PS fp 0 utf8len)
403 else do
404 fp' <- mallocByteString utf8len
405 withForeignPtr fp' $ \ptr' -> do
406 memcpy ptr' ptr (fromIntegral utf8len)
407 return (PS fp' 0 utf8len)
408#else
409encodeUtf8 (Text txt) = toByteString 0 (Just (byteLength buf - 1)) buf
410 where buf = js_encodeUtf8 txt
411#endif
413-- | Decode text from little endian UTF-16 encoding.
414decodeUtf16LEWith :: OnDecodeError -> ByteString -> Text
415decodeUtf16LEWith onErr bs = F.unstream (E.streamUtf16LE onErr bs)
416{-# INLINE decodeUtf16LEWith #-}
418-- | Decode text from little endian UTF-16 encoding.
419--
420-- If the input contains any invalid little endian UTF-16 data, an
421-- exception will be thrown. For more control over the handling of
422-- invalid data, use 'decodeUtf16LEWith'.
423decodeUtf16LE :: ByteString -> Text
424decodeUtf16LE = decodeUtf16LEWith strictDecode
425{-# INLINE decodeUtf16LE #-}
427-- | Decode text from big endian UTF-16 encoding.
428decodeUtf16BEWith :: OnDecodeError -> ByteString -> Text
429decodeUtf16BEWith onErr bs = F.unstream (E.streamUtf16BE onErr bs)
430{-# INLINE decodeUtf16BEWith #-}
432-- | Decode text from big endian UTF-16 encoding.
433--
434-- If the input contains any invalid big endian UTF-16 data, an
435-- exception will be thrown. For more control over the handling of
436-- invalid data, use 'decodeUtf16BEWith'.
437decodeUtf16BE :: ByteString -> Text
438decodeUtf16BE = decodeUtf16BEWith strictDecode
439{-# INLINE decodeUtf16BE #-}
441-- | Encode text using little endian UTF-16 encoding.
442encodeUtf16LE :: Text -> ByteString
443encodeUtf16LE txt = E.unstream (E.restreamUtf16LE (F.stream txt))
444{-# INLINE encodeUtf16LE #-}
446-- | Encode text using big endian UTF-16 encoding.
447encodeUtf16BE :: Text -> ByteString
448encodeUtf16BE txt = E.unstream (E.restreamUtf16BE (F.stream txt))
449{-# INLINE encodeUtf16BE #-}
451-- | Decode text from little endian UTF-32 encoding.
452decodeUtf32LEWith :: OnDecodeError -> ByteString -> Text
453decodeUtf32LEWith onErr bs = F.unstream (E.streamUtf32LE onErr bs)
454{-# INLINE decodeUtf32LEWith #-}
456-- | Decode text from little endian UTF-32 encoding.
457--
458-- If the input contains any invalid little endian UTF-32 data, an
459-- exception will be thrown. For more control over the handling of
460-- invalid data, use 'decodeUtf32LEWith'.
461decodeUtf32LE :: ByteString -> Text
462decodeUtf32LE = decodeUtf32LEWith strictDecode
463{-# INLINE decodeUtf32LE #-}
465-- | Decode text from big endian UTF-32 encoding.
466decodeUtf32BEWith :: OnDecodeError -> ByteString -> Text
467decodeUtf32BEWith onErr bs = F.unstream (E.streamUtf32BE onErr bs)
468{-# INLINE decodeUtf32BEWith #-}
470-- | Decode text from big endian UTF-32 encoding.
471--
472-- If the input contains any invalid big endian UTF-32 data, an
473-- exception will be thrown. For more control over the handling of
474-- invalid data, use 'decodeUtf32BEWith'.
475decodeUtf32BE :: ByteString -> Text
476decodeUtf32BE = decodeUtf32BEWith strictDecode
477{-# INLINE decodeUtf32BE #-}
479-- | Encode text using little endian UTF-32 encoding.
480encodeUtf32LE :: Text -> ByteString
481encodeUtf32LE txt = E.unstream (E.restreamUtf32LE (F.stream txt))
482{-# INLINE encodeUtf32LE #-}
484-- | Encode text using big endian UTF-32 encoding.
485encodeUtf32BE :: Text -> ByteString
486encodeUtf32BE txt = E.unstream (E.restreamUtf32BE (F.stream txt))
487{-# INLINE encodeUtf32BE #-}
489foreign import ccall unsafe "_hs_text_decode_utf8" c_decode_utf8
490 :: MutableByteArray# s -> Ptr CSize
491 -> Ptr Word8 -> Ptr Word8 -> IO (Ptr Word8)
493foreign import ccall unsafe "_hs_text_decode_utf8_state" c_decode_utf8_with_state
494 :: MutableByteArray# s -> Ptr CSize
495 -> Ptr (Ptr Word8) -> Ptr Word8
496 -> Ptr CodePoint -> Ptr DecoderState -> IO (Ptr Word8)
498foreign import ccall unsafe "_hs_text_decode_latin1" c_decode_latin1
499 :: MutableByteArray# s -> Ptr Word8 -> Ptr Word8 -> IO ()
501#ifndef __GHCJS__
502foreign import ccall unsafe "_hs_text_encode_utf8" c_encode_utf8
503 :: Ptr (Ptr Word8) -> ByteArray# -> CSize -> CSize -> IO ()
504#else
505foreign import javascript unsafe "h$textFromString" js_fromString :: JSString -> (# ByteArray#, Int# #)
506foreign import javascript unsafe "h$encodeUtf8($1)" js_encodeUtf8 :: JSString -> Buffer
507#endif